Home

Wisim

17 Apr 2017

Print all sub-array with 0 sum

Given an array of integers, print all subarrays having 0 sum.

For example,

Input:  { 4, 2, -3, -1, 0, 4 }
Sub-arrays with 0 sum are
{ -3, -1, 0, 4 }
{ 0 }
 
Input:  { 3, 4, -7, 3, 1, 3, 1, -4, -2, -2 }
Sub-arrays with 0 sum are
{ 3, 4, -7 }
{ 4, -7, 3 }
{ -7, 3, 1, 3 }
{ 3, 1, -4 }
{ 3, 1, 3, 1, -4, -2, -2 }
{ 3, 4, -7, 3, 1, 3, 1, -4, -2, -2 }

意思就是给定一个数组,找到所有和为0的子数组。

解法1:暴力求解

双重循环,从当前数组开始往后遍历,找到和为0的子数组。时间复杂度是O(n^3),其中O(n^2)用于遍历出子数组,O(n)用于计算和。

解法2:使用HashMap来解决

构造一个 Map<Integer, ArrayList> hashMap 用来存放 遍历数组,以当前位置(包含当前位置)之前的所有元素之和sum为key,如果hashMap中不存在这个key,则将这个key插入到hashMap中,并将这个元素所在位置插入到对应的ArrayList中。如果hashMap中已经存在这个key,则遍历key对应的ArrayList,以ArrayList中(每个元素所在位置+1)为起始值,当前元素所在位置为结束值,这之间的子数组的和就是0,接着还是要把当前位置插入到已经存在的key所对应的ArrayList中。

举个例子:数组{ 3, 4, -7, 3, 1, 3, 1, -4, -2, -2 }

我们来一步一步的分析:

(1)首先初始化的时候插入key为0,对应的ArrayList中插入-1,这样以后如果找到子数组就是从位置0开始了。插入之后hashMap的内容是:
{0,[-1]}

(2)位置0的元素为3,hashMap中不含(0+3=3)。插入之后hashMap的内容是:
{0,[-1]},
{3,[0]}

(3)位置1的元素是4,hashMap中不含(3+4=7),插入之后hashMap的内容是:
{0,[-1]},
{3,[0]},
{7,[1]}

(4)位置2的元素是-7,hashMap包含(-7+7=0),插入之后hashMap的内容是:
{0,[-1,2]},
{3,[0]},
{7,[1]}
并且此时有一个满足条件的子数组,对应的位置序列:
[0,1,2]

(5)位置3的元素是3,hashMap包含(0+3=3),插入之后hashMap的内容是:
{0,[-1,2]},
{3,[0,3]},
{7,[1]}
并且此时有一个满足条件的子数组,对应的位置序列:
[1,2,3]

(6)位置4的元素是1,hashMap不含(1+3=4),插入之后hashMap的内容是:
{0,[-1,2]},
{3,[0,3]},
{7,[1]},
{4,[4]}

(7)位置5的元素是3,hashMap包含(4+3=7),插入之后hashMap的内容是:
{0,[-1,2]},
{3,[0,3]},
{7,[1,5]},
{4,[4]}
并且此时有一个满足条件的子数组,对应的位置序列:
[2,3,4,5]

(8)位置6的元素是1,hashMap不含(7+1=8),插入之后hashMap的内容是:
{0,[-1,2]},
{3,[0,3]},
{7,[1,5]},
{4,[4]},
{8,[6]}

(9)位置7的元素是-4,hashMap包含(-4+8=4),插入之后hashMap的内容是:
{0,[-1,2]},
{3,[0,3]},
{7,[1,5]},
{4,[4,7]},
{8,[6]}
并且此时有一个满足条件的子数组,对应的位置序列:
[5,6,7]

(10)位置8的元素是-2,hashMap包含(-2+4=2),插入之后hashMap的内容是:
{0,[-1,2]},
{3,[0,3]},
{7,[1,5]},
{4,[4,7]},
{8,[6]},
{2,[8]}

(11)位置9的元素是-2,hashMap包含(-2+2=0),插入之后hashMap的内容是:
{0,[-1,2,9]},
{3,[0,3]},
{7,[1,5]},
{4,[4,7]},
{8,[6]},
{2,[8]}
并且此时有两个满足条件的子数组,对应的位置序列:
[0,1,2,3,4,5,6,7,8,9]
[3,4,5,6,7,8,9]


来总结一下这种解法的:这种解法的时间复杂度是O(n)。 初始化的时候向hashMap中插入了key为0的一个元素,这一步很重要。之后从数组中遍历出的元素累加,每次累加得到的结果肯定会发生变化。如果发现某一次累加之后的值之前已经出现过一次,说明什么,说明在第一次出现这个值之后中间的累加操作一定会使得第一次之后(不包含第一次)到第二次(包含第二次)的和的值为0。

结合下面这张折线图来看一下: rint_subarray

从-1位置开始,初始sum为0,经过了0,1,2三个位置的元素累加之后,sum又重新变为0。这期间发生了什么呢,依次发生了+3,+4,-7的操作,所以一旦发现两次的累加的和相等的话,表明这之间一定经过了和为0的加减操作。同理,其他几个位置的变化也是如此。

我们再用数学公式抽象一下:

设从 0 到 i 位置的累加和为 Si且i位置的元素为e[i],从 0 到 n 位置的累加和为 Sn且n位置的元素为e[n],Si = Sn。

从累加的定义我们知道:

于是可以得到:

又有:

所以 e[i+1] + e[i+2] + ... + e[n] = 0 。 也就是从 i+1 开始到 n 的子数组之和为0


Link : find-sub-array-with-0-sum


THE END.