降低复杂度以找到数组元素的绝对和

时间:2019-12-18 19:44:06

标签: java performance time-complexity

我在Hackerrank上遇到了这个问题。 https://www.hackerrank.com/challenges/playing-with-numbers/problem

给定一个整数数组,您必须回答许多查询。每个查询都由一个整数x组成,并按以下方式执行:

  • 将x添加到数组的每个元素,为以后的任何查询永久修改它。
  • 找到数组中每个元素的绝对值,并在新行上打印绝对值的和。

我需要完成以下方法

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

在这里,arr是具有n个元素的数组 和queries包含我需要与数组x的每个值相加的所有arr,然后获得arr元素的绝对和。因此,结果数组的大小将与数组queries相同,假设大小为m。该方法将返回m元素的数组。

下面是我的实现。

       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;
       }

它工作正常,复杂度为O(mn),但我需要以O(nlog_m)O(mlog_n)或类似的复杂度来做到这一点。

3 个答案:

答案 0 :(得分:1)

灵感是受 h4z3 在以下链接中给出的说明的启发, Absolute Elements Sums

我已经用Java实现了这个想法,

复杂度为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;
   }

答案 1 :(得分:0)

答案

从本质上讲,如果您知道有多少个数字为正数,有多少个数字为负数,则可以将这两个计数乘以累积的查询总数(对于负数,则为* -1)。

计算每个步骤中-/ +的总数以及所有这些值的总数。例如。对所有负数加1,直到所有负数为正;对所有负数加-1,直到所有正数为负,存储每个步骤的结果(-/ +计数和所有-/ +值的总和)。

您现在可以参考每一步的总和和-/ +总数,以计算出每个查询的结果。

您还将需要将playingWithNumbers方法和结果数组的返回类型从int更改为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;
}

答案 2 :(得分:-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;
       }

复杂度为O(n),额外的内存消耗为O(1)