2

I got this problem on Hackerrank. https://www.hackerrank.com/challenges/playing-with-numbers/problem

Given an array of integers, you must answer a number of queries. Each query consists of a single integer, x and is performed as follows:

  • Add x to each element of the array, permanently modifying it for any future queries.
  • Find the absolute value of each element in the array and print the sum of the absolute values on a new line.

All I need to complete the following method,

static int[] solution(int[] arr, int[] queries)

Here, arr is the array with n elements and queries contain all the x that I need to add with each value of the array arr and then get the absolute sum of the elements of arr. So the resultant array will be the same size as array queries, say that size is m. The method will return an array of m elements.

Below is my implementation.

       static int[] solution(int[] arr, int[] queries)
       {

          int[] result = new int[queries.length];

          for (int i = 0; i < queries.length; i++)
          {
             int total = 0;

             for (int j = 0; j < arr.length; j++)
             {
                arr[j] += queries[i];

                if (arr[j] > 0)
                   total += arr[j];
                else
                   total -= arr[j];
             }
             result[i] = total;
          }

          return result;
       }

It works fine, its complexity is O(mn), but I need to do it with a complexity of something O(nlog_m) or O(mlog_n) or close to that.

User_67128
  • 1,230
  • 8
  • 21
  • 1
    Questions asking for help optimizing code that is currently working are usually better suited to https://codereview.stackexchange.com. You might want to consider migrating your question over to there. – azurefrog Dec 18 '19 at 19:49
  • At first I asked there codereview.stackexchange.com, someone said since I'm looking for new implementation, I should ask here. So I delete that, and ask here. – User_67128 Dec 18 '19 at 19:52
  • You can keep `arr`'s sum in an external variable, along with the aggregated sum of the integers in `queries` – GalAbra Dec 18 '19 at 20:00
  • @azurefrog Not really, CR is more than just an optimization. There's not much of OP's code and there's no point in making it nicer it when it needs a full rewrite. – maaartinus Dec 19 '19 at 00:02

3 Answers3

1

Inspired by the explanation given by h4z3 in the following link, Absolute Elements Sums

I've implemented the idea in Java,

The complexity is O(n log n).

   static int bisect_left(int[] num, int x)
   {
      int low = 0;
      int high = num.length - 1;

      while (low < high)
      {
         int mid = (low + high) / 2;

         if (num[mid] >= x)
            high = mid - 1;
         else
            low = mid + 1;
      }

      return (num[low] < x) ? low + 1 : low;
   }

   static int[] solution(int[] arr, int[] queries)
   {
      Arrays.sort(arr); // O(n log n)

      int N = arr.length;

      int[] results = new int[queries.length];
      int[] sc = new int[N + 1];
      sc[0] = 0;
      sc[1] = arr[0];

      for (int i = 1; i < N; i++)
         sc[i + 1] = sc[i] + arr[i];

      int q = 0;
      for (int i = 0; i < queries.length; i++)  //  O(m)
      {
         q += queries[i];

         int n = bisect_left(arr, -q);  //  O(log n)

         results[i] = sc[N] + q * N - 2 * (sc[n] + q * n);
      }

      return results;
   }
abhimanyue
  • 196
  • 1
  • 12
0

ANSWER

Essentially, if you know how many numbers will be positive and how many will be negative, you can just multiply both of these counts by the accumulated query total (and *-1 for negatives).

Work out the total count for how many -/+ there are and the total sum for all these values at each step. E.g. add +1 to all until all negatives are positive, and add -1 to all until all positives are negative, store the results of each step (the -/+ counts and total sum of all -/+ values).

You can now reference the total sum and the total -/+ counts for each step to work out the result of each query.

You will also need to change the return type of both the playingWithNumbers method and the result array, from int to long!

static long[] playingWithNumbers(int[] arr, int[] queries) {

    long[] results = new long[queries.length];

    List<Integer> negatives = new ArrayList<>(arr.length);
    List<Integer> positives = new ArrayList<>(arr.length);

    long negativeSum = 0;
    long positiveSum = 0;
    for (int i : arr) {
        if (i < 0) {
            negatives.add(i);
            negativeSum += i;
        } else {
            positives.add(i);
            positiveSum += i;
        }
    }
    int negativeCount = negatives.size();
    int positiveCount = positives.size();
    Collections.sort(negatives);
    Collections.sort(positives);

    Map<Integer, Integer> countMap = new HashMap<>(arr.length);
    Map<Integer, Long> sumMap = new HashMap<>(arr.length);
    long totalSum = positiveSum + (negativeSum * -1);
    countMap.put(0, negativeCount);
    sumMap.put(0, totalSum);

    if (positiveCount != 0) {
        long tmpTotalSum = totalSum;
        int tmpNegativeCount = negativeCount;
        int increment = negativeCount - positiveCount;
        int index = 0;

        for (int i = 1; i <= positives.get(positiveCount - 1) + 1; i++) {
            while (index != positives.size() && positives.get(index) - i == -1) {
                tmpNegativeCount++;
                increment += 2;
                index++;
            }
            tmpTotalSum += increment;
            countMap.put(i * -1, tmpNegativeCount);
            sumMap.put(i * -1, tmpTotalSum);
        }
    }

    if (negativeCount != 0) {
        long tmpTotalSum = totalSum;
        int tmpNegativeCount = negativeCount;
        int increment = positiveCount - negativeCount;
        int index = negativeCount - 1;

        for (int i = 1; i <= (negatives.get(0) - 1) * -1; i++) {
            int incrementNxt = 0;
            while (index != -1 && negatives.get(index) + i == 0) {
                tmpNegativeCount--;
                incrementNxt += 2;
                index--;
            }
            tmpTotalSum += increment;
            increment += incrementNxt;
            countMap.put(i, tmpNegativeCount);
            sumMap.put(i, tmpTotalSum);
        }
    }

    int maxNegative = positiveCount != 0 ? (positives.get(positiveCount - 1) + 1) * -1 : 0;
    int maxPositive = negativeCount != 0 ? ((negatives.get(0) - 1)) * -1 : 0;
    int totalCount = positiveCount + negativeCount;
    long accumulatedTotal = 0;

    for (int i = 0; i < queries.length; i++) {
        accumulatedTotal += queries[i];

        if (accumulatedTotal >= maxNegative && accumulatedTotal <= maxPositive) {
            results[i] = sumMap.get((int)accumulatedTotal);
        } else if (accumulatedTotal < maxNegative) {
            long extra = maxNegative - accumulatedTotal;
            results[i] = sumMap.get(maxNegative) + countMap.get(maxNegative) * extra;
        } else {
            long extra = accumulatedTotal - maxPositive;
            results[i] = sumMap.get(maxPositive) + (totalCount - countMap.get(maxPositive)) * extra;
        }
    }

    return results;
}
RDONALDSON
  • 57
  • 1
  • 4
-1


       static int[] solution(int[] arr, int[] queries)
       {

          int querySum = 0;
          for (int i; i < queries.length; i++) {
            querySum += queries[i];
          }

          for (int j; j < arr.length; j++) {
            arr[j] += querySum;
            arr[j] = Math.abs(arr[j]);
          }

          return arr;
       }

The complexity is O(n), additional memory consumption O(1)

Alexey
  • 2,388
  • 1
  • 16
  • 32