Print all sub-array with 0 sum

本文介绍了一种高效算法,用于在整数数组中查找所有和为零的子数组。通过使用HashMap,算法能在O(n)时间内解决问题,避免了传统暴力解法的高时间复杂度。文章详细解释了算法原理,并通过实例演示了其工作流程。

题目:

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(n3),其中O(n2)用于遍历出子数组,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。

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

从-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。

从累加的定义我们知道:

S i + e [ i + 1 ] = S i + 1 S_{i} + e[i+1] = S_{i+1} Si+e[i+1]=Si+1

于是可以得到:

S i + e [ i + 1 ] + e [ i + 2 ] + . . . + e [ n ] = S n S_i + e[i+1] + e[i+2] + ... + e[n] = S_n Si+e[i+1]+e[i+2]+...+e[n]=Sn

又有: S i = S n S_i = S_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.

outputJSON = cJSON_Duplicate(paged_req_cache->output_json, 1); if(outputJSON == NULL) { PRINTF_ECHO("[Error] %s cache paged get request(0x%04x) duplicate failed when parsing cache!", __FUNCTION__, pagedConfig->OpCode); ret = TPAPP_ERROR; goto leave; } outputSumJSON = cJSON_GetObjectItem(outputJSON, pagedConfig->sum_field); if(outputSumJSON == NULL || outputSumJSON->type != cJSON_Number) { PRINTF_ECHO("[Error] %s paged get request(0x%04x) that output cache json doesn't contain %s key,or not a number when parsing cache!", __FUNCTION__, pagedConfig->OpCode, pagedConfig->sum_field); ret = TPAPP_ERROR; goto leave; } outputArrayJSON = cJSON_GetObjectItem(outputJSON, pagedConfig->array_field); if(outputArrayJSON == NULL || outputArrayJSON->type != cJSON_Array) { PRINTF_ECHO("[Error] %s paged get request(0x%04x) that output cache json doesn't contain %s key,or not an array when parsing cache!", __FUNCTION__, pagedConfig->OpCode, pagedConfig->array_field); ret = TPAPP_ERROR; goto leave; } sub_list = cJSON_DetachSubArrayFromArray(outputArrayJSON, inputStartIndexJSON->valueint, inputAmountJSON->valueint); if(sub_list == NULL || sub_list->type != cJSON_Array) { PRINTF_ECHO("[Error] %s paged get request(0x%04x) cJSON_DetachSubArrayFromArray return null when parsing cache!", __FUNCTION__, pagedConfig->OpCode); ret = TPAPP_ERROR; goto leave; } amount = cJSON_GetArraySize(sub_list); cJSON_ReplaceItemInObject(outputJSON, pagedConfig->array_field, sub_list); cJSON_ReplaceItemInObject(outputJSON, pagedConfig->start_index_field, cJSON_CreateNumber(inputStartIndexJSON->valueint)); cJSON_ReplaceItemInObject(outputJSON, pagedConfig->amount_field, cJSON_CreateNumber(amount)); outData = cJSON_Print(outputJSON); 增加循环判断条件,当sub_list替换outputJSON->array_field后 如果strlen(cJSON_Print(outputJSON)) > MAX则 1. inputAmountJSON->valueint 减半,重新获取sub_list ,并替换outputJSON->array_field 2. 是否需要重新 cJSON_Duplicate 获取outputJSON的副本?
最新发布
12-18
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值