【问题标题】:Reducing complexity to find the absolute sum of an array elements降低复杂性以找到数组元素的绝对和
【发布时间】:2019-12-18 19:44:06
【问题描述】:

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

给定一个整数数组,您必须回答多个查询。每个查询由一个整数 x 组成,执行如下:

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

我只需要完成下面的方法,

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

这里,arr 是包含 n 元素的数组 和queries 包含我需要与数组arr 的每个值相加的所有x,然后得到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) 或接近的复杂度来做。

【问题讨论】:

  • 寻求帮助以优化当前工作的代码的问题通常更适合codereview.stackexchange.com。您可能需要考虑将您的问题迁移到那里。
  • 一开始我在codereview.stackexchange.com上问过,有人说我在找新的实现,所以我应该在这里问。所以我把它删了,然后在这里问。
  • 您可以将arr 的总和与queries 中的整数总和一起保存在一个外部变量中
  • @azurefrog 并非如此,CR 不仅仅是一种优化。 OP 的代码不多,当需要完全重写时,没有必要让它变得更好。

标签: java performance time-complexity


【解决方案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;
   }

【讨论】:

  • 这是一个聪明的解决方案。谢谢。
【解决方案2】:

回答

基本上,如果您知道有多少是正数,有多少是负数,您只需将这两个计数乘以累计查询总数(*-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;
}

【讨论】:

  • 能否请您添加几句话来描述您的想法/技术。我想以我的方式实现它。谢谢。
  • 我已经用更详细的伙伴更新了我的答案。谢谢。
  • 你的程序产生了错误的结果。对于 arr[] = {0, -2, -3, 2} 和​​ queries[] = {-2, 4, -1} 输出应该是 {11, 7, 7} 但您的代码会产生 {9, 9, 7}。您是否使用一些样本输入检查了您的结果?
  • 是的,多个测试用例,它也通过了Hackerrank上的所有测试用例。我在我的机器上得到 {11, 7, 7} 结果。检查您是否正确复制了代码。
  • 我的机器上得到了 {11, 7, 7} 结果。检查您是否正确复制了代码。
【解决方案3】:


       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)

【讨论】:

  • 你错过了“绝对价值”。
  • 嗯,是的,你是对的,但它不会改变复杂性和内存消耗
  • 它仍然是错误的@Alexey,对于 arr[] = {0, -2, -3, 2} 和​​ queries[] = {-2, 4, -1} 输出应该是 {11 , 7, 7},无论查询大小如何,您的答案都会产生大小为 4 的数组。
猜你喜欢
  • 2019-06-01
  • 1970-01-01
  • 2018-04-15
  • 1970-01-01
  • 2021-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-10
相关资源
最近更新 更多