题目:给定一个数组arr,该数组无序,但每个值均为正数,再给定一个正数k。求arr得所有子数组中所有元素相加和为k的最长子数组长度。
解答:最优解可以做到时间复杂度为o(n),额外空间复杂度为o(1).首先用两个位置来标记子数组的左右两头,记为left和right,开始时都在数组的最左边(left=0,right=0)。整体过程如下:
1.开始时变量left=0,right=0,代表子数组arr[left......right].
2.变量sum始终表示子数组arr[left......right]的和。开始时,sum=arr[0],即arr[0....0]的和。
3.变量len一直记录累加和为k的所有子数组中最大子数组的长度。开始时,len=0.
4.根据sum与k的比较结果决定是left移动还是right移动,具体如下:
(1)如果sum==k,说明arr[left.....right]累加和为k,如果arr[left.....right]长度大于len,则跟新len,此时因为数组所有的值都为正数,那么所有从left位置开始,在right之后的位置结束的子数组,累加和一定大于k。所以,令left加1,这表示我们开始考察以left之后的位置开始的子数组,同时令sum=sum-arr[left],sum此时表示arr[left+1.....right]
(2)如果sum小于k,说明arr[left.....right]还需要加上right后面的值,其和才可能达到k,所以,令right+1,sum+=arr[right].需要注意的是,right+1后是否越界。
(3)如果sum大于k,说明所有从left位置开始,在right之后的位置结束的子数组,累计和一定大于k,因此,我们考查以left之后的位置开始的子数组,同时令sum-=arr[left],sum此时表示arr[left+1....right]的累计和。
5.如果right<arr.length,重复步骤4.否则直接返回len,全部过程结束。
具体的参考代码如下:
1 public int getMaxLength(int[] arr,int k) 2 { 3 if(arr==null || arr.length==0 || k<=0) 4 return 0; 5 int left=0; 6 int right=0; 7 int sum=arr[0]; 8 int len=0; 9 while(right<arr.length) 10 { 11 if(sum==k) 12 { 13 len=Math.max(len,right-left+1); 14 sum-=arr[left++]; 15 }else if(sum<k) 16 { 17 right++; 18 if(right==arr.length) 19 break; 20 sum+=arr[right]; 21 } 22 else 23 sum-=arr[left++]; 24 } 25 26 return len; 27 }