运行时错误&时间复杂度问题:最小化值|(A [0] + ... + A [P-1]) - (A [P] + ... + A [N-1])|

时间:2014-10-07 06:24:03

标签: javascript big-o time-complexity array-algorithms

我最近解决了编码问题。我想出了以下问题的解决方案。

  

给出了由N个整数组成的非空零索引数组A.数组A表示磁带上的数字   任何整数P,使得0 <0。 P&lt; N,将此磁带分成两个非空部分:A [0],A [1],...,A [P-1]和A [P],A [P + 1],...,A [N - 1]。
  两部分之间的差异是:|(A [0] + A [1] + ... + A [P - 1]) - (A [P] + A [P + 1] + .. 。+ A [N - 1])|
  换句话说,它是第一部分之和与第二部分之和的绝对差值   例如,考虑阵列A使得:
    A [0] = 3
    A [1] = 1
    A [2] = 2
    A [3] = 4
    A [4] = 3
  我们可以将这个磁带分成四个位置:
  P = 1,差异= | 3 - 10 | = 7
  P = 2,差异= | 4 - 9 | = 5
  P = 3,差异= | 6 - 7 | = 1
  P = 4,差异= | 10 - 3 | = 7
  
  写一个函数:   功能解决方案(A);
  如果给定N个整数的非空零索引数组A,则返回可以实现的最小差异   例如,给定:
    A [0] = 3
    A [1] = 1
    A [2] = 2
    A [3] = 4
    A [4] = 3
  如上所述,该函数应返回1   假设:
  N是[2..100,000]范围内的整数;
  数组A的每个元素都是[-1,000..1,000]范围内的整数   
  复杂性:
  预期的最坏情况时间复杂度是O(N);
  预期的最坏情况空间复杂度是O(N),超出输入存储(不计算输入参数所需的存储空间)   可以修改输入数组的元素   

以下是我从测试解决方案中获得的反馈:

  

正确性:   small_range范围序列,长度= ~1,000 1.900 s
运行时错误   测试程序意外终止了   性能:   检测到的时间复杂度:O(N * N)

所以我在1000左右的范围内得到一个运行时错误。最重要的是,我没有得到O(n)。当我使用嵌套for循环时,我得到O(n * n)。

(1)如何修复运行时错误?
(2)如何为同一问题构造O(n)算法?有什么建议吗?

这是我的解决方案:

    function solution(A){
        var len = A.length;
        var diff = [];  // Array to store the differences
        var sumLeft = 0;    // Sum of array elements from index 0 to index p - 1
        var sumRight = 0;   // Sum of array elements from index p to index n - 1
        for(var p = 1; p < len; p++){
            sumLeft = 0;
            sumRight = 0;
            // Calculate sumLeft:
            for(var i = 0; i < p; i++){
                sumLeft += A[i];
            }
            // Calculate sumRight:
            for(var j = p; j < len; j++){
                sumRight += A[j];
            }
            // Calculate differences, compute absolute values, and push into diff array:
            diff.push(Math.abs(sumLeft - sumRight));
        }
        // Return the minimum of diff array by sorting it and returning the first element:
        return bubbleSort(diff)[0];
    }

    function bubbleSort(array){
        var len = array.length;
        for(var i = 0; i < len; i++){
            for(var j = i + 1; j < len; j++){
                if(array[i] > array[j]){
                    var temp = array[i];
                    array[i] = array[j];
                    array[j] = temp;
                }
            }
        }
        return array;
    }

4 个答案:

答案 0 :(得分:0)

测试P的新值时,无需计算向量片段的总和。如果您为leftSum的两个部分计算了rightSumP=(p-1),那么当您需要为P=p计算时,您只需:

  • array[p]移除rightSum;和
  • array[p]添加到leftSum

这两者都是O(1)。如果你这样做(n-1)次,你仍然处于O(n)复杂度。

希望有所帮助。

答案 1 :(得分:0)

让我试着解释一下如何考虑改进算法的空间和时间复杂度。您清楚地意识到您使用嵌套for循环,这会大大增加迭代次数,并且还可能导致足够大的输入时出现运行时错误。

第一步应该是减少操作的冗余。现在,您为left的不同值重复计算rightp总和。你完全不需要它。我将举例说明算法的流程:

 Array indices -> A [0, 1, 2, 3, ....,p ,p+1, ....n-1] for a size n array

 At any point A[p] would act as a pivot as it breaks the array into two.
 For p = 1, You just take the first element i.e A[0] and the right part of the sum is
 A[1] + A[2] + .... A[n-1]

 Let S1 = A[0] and S2 = A[1] + A[2] + .... A[n-1] for p = 1
 The pivot or the break point here is A[p] i.e A[1]

 Calculate the absolute difference |S1- S2| and store it in a variable min-diff

 For p = 2, 

 S1 will simply be S1 + A[1] i.e the previous value of S1 including the last pivot 

 S2 = S2 - A[1], as we have moved on to the next element. 
 The sum of the remaining elements would not account the element we just crossed.

Formally,

S1 = S1 + A[p-1] and S2 = S2 - A[p-1]

Calculate the new difference i.e |S1 - S2| and just check 
if it is smaller than the value of our variable min-diff. 
If it is, update the value of min-diff with the present difference, 
otherwise move on to the next element.

At any value of p, S1 represents sum of left half, 
S2 represents sum of right half and 
min-diff represents the minium absolute difference till now.

此算法的复杂性

  1. 时间复杂度

    我们第一次计算元素总和的唯一时间 当我们计算A [1] + ... A [n-1]时。在那之后我们只是穿越了 数组的元素一个接一个。

    所以我们遍历两次最大数组的元素。所以时间 复杂性显然是 O(N)

  2. 空间复杂性

    我们使用三个额外的变量,即S1,S2和min-diff 这个算法累加和并存储最小绝对值 差异以及p的值和n的元素 阵列。

    因此该算法的空间复杂度再次 O(N)

  3. 侧面说明 - 虽然您根本不需要对此问题进行排序,因为您只需输出最小差异,但每次排序时,请不要使用冒泡排序它显然是效率最低的分选方法。使用合并排序或快速排序,运行时间 O(NlogN)

    ,情况会更好

    我希望我能解释自己。尝试将其编码为一个简单的函数,不应该花费很长时间。它应该也可以修复运行时错误。

答案 2 :(得分:0)

java代码:O(N)

import java.math.*;

class Solution {

  public int solution(int[] A) {

    long sumright = 0;
    long sumleft = 0;
    long ans;

    for (int i =1;i<A.length;i++)
    {
      sumright += A[i];
    }

    sumleft = A[0];
    ans =Math.abs(sumright+sumleft);

    for (int P=1; P<A.length; P++)
    {
      if (Math.abs(sumleft - sumright)<ans)
      {
        ans = Math.abs(sumleft - sumright);
      }
      sumleft += A[P];
      sumright -=A[P];
    }
  return (int) ans;
}

}

答案 3 :(得分:0)

无需调试,此解决方案在Codility上的任务得分为100%(正确性和性能均为100%):

function solution(A) {
    var sum_right = 0;

    for (int of A.slice(1)) {
        sum_right += int;
    }

    var sum_left = A[0];
    var diff_of_sums = sum_left - sum_right;
    var lowest_diff = Math.abs(diff_of_sums);
    var diff_new;
    // we assume the length is at least 2
    if (A.length == 2) {
        return lowest_diff;
    }
    for (var int of A.slice(1)) {
        diff_new = Math.abs(sum_left - sum_right);
        if (diff_new < lowest_diff) {
            lowest_diff = diff_new;
        }
        sum_left += int;
        sum_right -= int;
    }
    return lowest_diff;
}

具有调试功能:

// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');

function solution(A) {
    var sum_right = 0;

    for (int of A.slice(1)) {
        sum_right += int;
    }

    var sum_left = A[0];
    var diff_of_sums = sum_left - sum_right;
    // var total = Math.abs(sum_left + sum_right);
    var lowest_diff = Math.abs(diff_of_sums);
    var diff_new;
    // we assume the length is at least 2
    if (A.length == 2) {
        return lowest_diff;
    }
    // console.log("lowest_diff initially:", lowest_diff)
    // var diff_of_sums_new = diff_of_sums;
    // console.log("diff_of_sums initially:", diff_of_sums)
    // console.log("A.slice(1):", A.slice(1))
    for (var int of A.slice(1)) {
        // console.log("lowest_diff", lowest_diff)
        diff_new = Math.abs(sum_left - sum_right);
        if (diff_new < lowest_diff) {
            lowest_diff = diff_new;
        }
        sum_left += int;
        sum_right -= int;
    }
    //           if (Math.abs(sumleft - sumright)<ans)
    //   {
    //     ans = Math.abs(sumleft - sumright);
    //   }
    //   sumleft += A[P];
    //   sumright -=A[P];
    //     // console.log("int === -1:", int === -1);
    //     // diff_of_sums = diff_of_sums_new;
    //     console.log("lowest_diff =", lowest_diff);
    //     // console.log("A[index + 1] =", A[parseInt(index) + 1]);
    //     // console.log("parseInt(index) === 1", parseInt(index) === 1)
    //     diff_of_sums = Math.abs(lowest_diff - 2 * Math.abs(int));
    //     // console.log("diff_of_sums =", diff_of_sums);
    //     // console.log("diff_of_sums = Math.abs(diff_of_sums - 2 * A[index + 1]) = ", diff_of_sums_new);
    //     if (diff_of_sums < lowest_diff) {
    //         lowest_diff = diff_of_sums;
    //         // console.log("lowest_diff = diff_of_sums =", diff_of_sums_new)
    //     } else {
    //         return lowest_diff;
    //     }
    // }
    // console.log("lowest_diff before returning", lowest_diff);
    return lowest_diff;
}
// Note that it's better to use test cases in Codility for this, but I've left here to show some.
// console.log("solution([-1000, 1000])", solution([-1000, 1000]));
// console.log("solution([2, 7, 20, 30, 1])", solution([2, 7, 20, 30, 1])); // sum 60, smallest diff = |29 - 31| = 2
// console.log("solution([-2, -7, -20, -30, -1])", solution([-2, -7, -20, -30, -1])); // sum -60, smallest diff = 2
// console.log("solution([-1, -1]):", solution([-1, -1]));
// console.log("solution([-2, -1]):", solution([-2, -1]));
// console.log("solution([-2, -1, -3]):", solution([-2, -1, -3]));
// console.log("solution([]):", solution([]))

最初,我尝试从中途开始,但这使实现更加复杂。这是我在放弃该方法之前想出的(而且我不会为解决某个问题而烦恼):

function solution(A) {
    // const sum = A.reduce((partial_sum, a) => partial_sum + a); 
    // console.log(sum);
    var size = A.length;
    if (size % 2 == 0) {
        mid = size/2;
    } else {
        mid = Math.floor(size/2);
    }
    console.log("mid initially", mid);
    var sum1 = A.slice(0, mid).reduce((partial_sum, a) => partial_sum + a);
    // console.log("sum1:",sum1);
    var sum2 = A.slice(mid).reduce((partial_sum, a) => partial_sum + a);
    // console.log("sum2:", sum2);
    var sum_diff = sum1 - sum2;
    // console.log("sum_diff:", sum_diff);
    if (sum_diff === 0) {
        return sum_diff;
    }
    // sum_diff = function() {Math.abs(sum2 - sum1)};
    // sum_diff = sum_diff();
    var lowest_diff = Math.abs(sum_diff);
    var diff_negative = (sum_diff < 0);
    console.log("diff_negative initially:", diff_negative)
    var crossed_over = false;
    var sum_diff_new;
    while (diff_negative) {
        mid++;
        if (mid === size) {
            return lowest_diff;
        }
        // var sum1_new = sum1 + A[mid];
        // var sum2_new = sum2 - A[mid];
        // sum_diff_new = sum1_new - sum2_new = sum1 - sum2 + 2*A[mid] = sum_diff - 2*A[mid];
        sum_diff_new = sum_diff - 2*A[mid];
        diff_negative = (sum_diff_new < 0);
        if (diff_negative = false) {
            crossed_over = true;
            if (lowest_diff <= sum_diff_new) {
                return lowest_diff;
            } else {
                return sum_diff_new;
            }
        }
    }

    while(!diff_negative) {
        mid--;
        if (mid === -1) {
            return lowest_diff;
        }
        // var sum1_new = sum1 - A[mid];
        // var sum2_new = sum2 + A[mid];
        // sum_diff_new = sum1_new - sum2_new = sum1 - sum2 - 2*A[mid] = sum_diff - 2*A[mid];
        console.log("sum_diff:", sum_diff);
        sum_diff_new = sum_diff + 2*A[mid];
        console.log("sum_diff_new:", sum_diff_new);
        diff_negative = (sum_diff_new < 0);
        if (diff_negative = true) {
            crossed_over = true;
            var sum_diff_new_pos = Math.abs(sum_diff_new);
            if (lowest_diff <= sum_diff_new_pos) {
                return lowest_diff;
            } else {
                return sum_diff_new_pos;
            }
        }
    }
}

// Issues: doesn't work e.g. with  [-2, -1, -3] and [-2, -7, -20, -30, -1]