最大子数组,其总和等于0

时间:2011-04-04 02:37:01

标签: algorithm

数组包含正元素和负元素,找到其总和等于0的最大子数组。

12 个答案:

答案 0 :(得分:70)

当前接受的答案中的链接需要注册会员资格,而不是其内容。

该算法将找到所有和0的子阵列,并且可以很容易地修改它以找到最小的子阵列或跟踪起始和结束索引。该算法 O(n)

给定一个int[] input数组,您可以创建一个int[] tmp数组,其中tmp[i] = tmp[i - 1] + input[i]; tmp的每个元素将存储该元素的输入总和(数组的前缀和)。

现在,如果你检查tmp,你会注意到可能存在彼此相等的值。假设这个值在索引j an k with j < k处,那么直到j的输入总和等于直到k的总和,这意味着数组之间的总和jk为0!具体而言,0和子阵列将从索引j + 1到k。

  • 注意:如果是j + 1 == k,那么k is 0就是这样! ;)
  • 注意:算法应考虑虚拟tmp[-1] = 0;
  • 注意:一个空数组的总和为0,它是最小的,这个特殊情况也应该在面试中提出来。然后面试官会说这不算数,但这是另一个问题! ;)

实现可以通过不同的方式完成,包括使用带对的HashMap,但要注意上面的NOTE部分中的特殊情况。

示例:

int[] input = {4,  6,  3, -9, -5, 1, 3, 0, 2}
int[] tmp =   {4, 10, 13,  4, -1, 0, 3, 3, 5}
  • 索引0和3的tmp中的值4 ==&gt; sum tmp 1到3 = 0,长度(3 - 1)+ 1 = 3
  • 索引5的tmp中的值0 ==&gt; sum tmp 0到5 = 0,长度(5 - 0)+ 1 = 6
  • 指数6和7的tmp中的值3 ==&gt; sum tmp 7到7 = 0,长度(7-7)+ 1 = 1

**** **** UPDATE

假设在我们的tmp数组中我们最终得到具有相同值的多个元素,那么你必须考虑其中的每个相同的对!示例(请记住索引'-1'处的虚拟'0'):

int[] array = {0, 1, -1, 0}
int[] tmp = {0, 1, 0, 0}

通过应用上述相同的算法,0-sum子阵列由以下索引(包括)分隔:

[0] [0-2] [0-3] [1-2] [1-3] [3]

虽然具有相同值的多个条目的存在可能会影响算法的复杂性,具体取决于实现,但我相信通过在tmp上使用反向索引(将值映射到它出现的索引),我们可以保留在O(n)的运行时间。

答案 1 :(得分:7)

这与Gevorg建议的行相同,但我使用哈希映射进行快速查找。 O(n)复杂性虽然使用了额外的空间。

private static void subArraySumsZero()
{
    int [] seed = new int[] {1,2,3,4,-9,6,7,-8,1,9};
    int currSum = 0;
    HashMap<Integer, Integer> sumMap = new HashMap<Integer, Integer>();
    for(int i = 0 ; i < seed.length ; i ++)
    {
        currSum += seed[i];
        if(currSum == 0)
        {
            System.out.println("subset : { 0 - " + i + " }");
        }
        else if(sumMap.get(currSum) != null)
        {
            System.out.println("subset : { " 
                                + (sumMap.get(currSum) + 1) 
                                + " - " + i + " }");
            sumMap.put(currSum, i);
        }
        else
            sumMap.put(currSum, i);
    }
    System.out.println("HASH MAP HAS: " + sumMap);
}

生成的输出具有元素索引(基于零):

subset : { 1 - 4 }
subset : { 3 - 7 }
subset : { 6 - 8 }

答案 2 :(得分:5)

1. Given A[i]
  A[i] | 2 |  1 | -1 | 0 | 2 | -1 | -1
-------+---|----|--------|---|----|---
sum[i] | 2 |  3 |  2 | 2 | 4 |  3 |  2

2. sum[i] = A[0] + A[1] + ...+ A[i]
3. build a map<Integer, Set>
4. loop through array sum, and lookup map to get the set and generate set, and push <sum[i], i> into map.

Complexity O(n)

答案 3 :(得分:2)

这是我的实现,这是显而易见的方法,所以它可能是次优化的,但至少它是明确的。如果我错了,请纠正我。

从数组的每个索引开始,计算并比较各个和(tempsum)与所需的总和(在这种情况下,sum = 0)。由于整数是有符号的,我们必须计算每种可能的组合。

如果您不需要完整的子数组列表,您可以始终在内部循环中放置条件以突破它。 (假设你只是想知道这个子数组是否存在,只要tempsum = sum时返回true。)

public static string[] SubArraySumList(int[] array, int sum)
{
    int tempsum;
    List<string> list = new List<string>();
    for (int i = 0; i < array.Length; i++)
    {
        tempsum = 0;
        for (int j = i; j < array.Length; j++)
        {
            tempsum += array[j];
            if (tempsum == sum)
            {
                list.Add(String.Format("[{0}-{1}]", i, j));
            }
        }
    }
    return list.ToArray();
}

调用该函数:

int[] array = SubArraySumList(new int { 0, -1, 1, 0 }, 0));

打印输出数组的内容:

[0-0], [0-2], [0-3], [1-2], [1-3], [3-3]

答案 4 :(得分:1)

以下解决方案在不使用动态编程的情况下找到具有给定总和k的最大长度子阵列,但使用简单的递归。这里i_s是起始索引,i_e是sum

的当前值的结束索引
##Input the array and sum to be found(0 in your case)
a = map(int,raw_input().split())
k = int(raw_input())

##initialize total sum=0
totalsum=0

##Recursive function to find max len 0
def findMaxLen(sumL,i_s,i_e):
    if i_s<len(a)-1 and i_e>0: 
        if sumL==k:
            print i_s, i_e
            return (i_s,i_e)
        else:
            x = findMaxLen(sumL-a[i_s],i_s+1,i_e)
            y = findMaxLen(sumL-a[i_e],i_s,i_e-1)
            if x[1]-x[0]>y[1]-y[0]:
                return x
            else:
                return y
    else:
        ##Result not there
        return (-1,-1)


## find total sum
for i in range(len(a)):
    totalsum += a[i]

##if totalsum==0, max array is array itself
if totalsum == k:
    print "seq found at",0,len(a)-1

##else use recursion
else:
    print findMaxLen(totalsum,0,len(a)-1)

由于递归内存堆栈,时间复杂度为O(n),空间复杂度为O(n)

答案 5 :(得分:1)

以下是

中的O(n)实施

我们的想法是迭代给定的数组,并为每个元素arr[i],计算从0i的元素总和,将每个sum存储在{{1}中}}

  • 如果元素为HashMap,则将其视为 ZeroSum 子数组。

  • 如果0成为sum,则会有一个 ZeroSum 子数组,从00

  • 如果之前在i中看到了当前总和,那么就会有一个 ZeroSum 子数组,从那一点到HashMap

代码:

i

请在此处查看实验:http://rextester.com/PAKT41271

答案 6 :(得分:0)

数组包含正数和负数。找到具有最大总和

的子数组
public static int findMaxSubArray(int[] array)
{
    int max=0,cumulativeSum=0,i=0,start=0,end=0,savepoint=0;
    while(i<array.length)
    {
        if(cumulativeSum+array[i]<0)
        {
            cumulativeSum=0;
            savepoint=start;
            start=i+1;
        }
        else
            cumulativeSum=cumulativeSum+array[i];
        if(cumulativeSum>max)
        {
                max=cumulativeSum;
                savepoint=start;
                end=i;
        }
        i++;
    }

    System.out.println("Max : "+max+"  Start indices : "+savepoint+"  end indices : "+end);
    return max;

}

答案 7 :(得分:0)

下面的代码可以找出每个可能的子数组,它们的总和是给定的数字,并且(当然)它可以找出那种最短和最长的子数组。

public static void findGivenSumSubarray(int arr[], int givenSum) {
    int sum = 0;
    int sStart = 0, sEnd = Integer.MAX_VALUE - 1;  // Start & end position of the shortest sub-array
    int lStart = Integer.MAX_VALUE - 1, lEnd = 0;  // Start & end position of the longest  sub-array

    HashMap<Integer, ArrayList<Integer>> sums = new HashMap<>();
    ArrayList<Integer> indices = new ArrayList<>();

    indices.add(-1);
    sums.put(0, indices);

    for (int i = 0; i < arr.length; i++) {
        sum += arr[i];
        indices = sums.get(sum - givenSum);
        if(indices != null) {
            for(int index : indices) {
                System.out.println("From #" + (index + 1) + " to #" + i);
            }
            if(i - indices.get(indices.size() - 1) < (sEnd - sStart + 1)) {
                sStart = indices.get(indices.size() - 1) + 1;
                sEnd = i;
            }
            if(i - indices.get(0) > (lEnd - lStart + 1)) {
                lStart = indices.get(0) + 1;
                lEnd = i;
            }
        }
        indices = sums.get(sum);
        if(indices == null) {
            indices = new ArrayList<>();
        }
        indices.add(i);
        sums.put(sum, indices);
    }

    System.out.println("Shortest sub-arry: Length = " + (sEnd - sStart + 1) + ", [" + sStart + " - " + sEnd + "]");
    System.out.println("Longest  sub-arry: Length = " + (lEnd - lStart + 1) + ", [" + lStart + " - " + lEnd + "]");
}

答案 8 :(得分:0)

希望这对你有所帮助。

IsAlive

答案 9 :(得分:0)

解决方案之一:

假设我们有一个整数数组, int [] arr = {2,1,-1,-2};

我们将使用for循环遍历,直到找到数字<0或<= 0 i = 2;

通过内部循环,我们遍历将值赋给j = i-1 因此,我们可以找到正值。

for(int i = 0; i<arr.length; i++){
      int j = 0;
      int sum = arr[i];
      if(arr[i] < 0){
      j = i - 1;
}

我们将有一个sum变量,该变量将维护arr [i]和arr [j]之和并更新结果。

如果总和<0,那么我们必须向左移动数组,因此,我们将j减1,j-

for(j = i-1; j>=0; j--) {
      sum = sum + arr[j];
      if(sum == 0){
      System.out.println("Index from j=" + j+ " to i=" + i);
      return true;
   }
}

如果总和> 0,那么我们必须向右移动数组,因此,我们将增加i

当发现总和== 0时,我们可以打印j和i索引并返回或中断循环。

因此,它在线性时间内完成。同样,我们也不需要使用任何其他数据结构。

答案 10 :(得分:0)

该问题的另一种解决方案可能是: 1.计算整个数组的和 2.现在按照以下公式获得总和为零的最大子数组:

Math.max(find(a,l+1,r,sum-a[l]), find(a,l,r-1,sum-a[r]));
where l=left index, r= right index, initially their value=0 and a.length-1

想法很简单,我们可以通过sum = 0获得的最大大小是数组的大小,然后我们开始从左和右递归地跳过元素,在我们获得sum = 0的那一刻我们就停止了。下面是相同的代码:

static int find(int a[]) {
    int sum =0;
    for (int i = 0; i < a.length; i++) {
        sum = sum+a[i];
    }

    return find(a, 0, a.length-1, sum);
}


static int find(int a[], int l, int r, int sum) {
    if(l==r && sum>0) {
        return 0;
    }
    if(sum==0) {
        return r-l+1;
    }
    return Math.max(find(a,l+1,r,sum-a[l]), find(a,l,r-1,sum-a[r]));

}

答案 11 :(得分:-2)

希望这会有所帮助。

int v[DIM] = {2, -3,  1,  2,  3,  1,  4, -6,  7, -5, -1};
int i,j,sum=0,counter=0;

    for (i=0; i<DIM; i++) {
        sum = v[i];
        counter=0;
        for (j=i+1; j<DIM;j++) {
            sum += v[j];
            counter++;
            if (sum == 0) {
                printf("Sub-array starting from index %d, length %d.\n",(j-counter),counter +1);
            }
        }
    }