剑指 Offer 57 - II. 和为s的连续正数序列
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2:
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
思路:
解法一:一般解法
从i=1开始, 尝试每个值i为连续子数组的开头,
使用一个内层for循环判断这个开头能走到哪里,会有两种情况
第一种:sum[i…j]=target,记录答案,尝试下一个开头(因为每一个开头只会有为一个子数组的和是target);
第二种:sum[i…j]>target, 表示开头i无法产生“和为target的连续子数组”,于是尝试下一个开头。
public int[][] findContinuousSequence(int target){
List<int[]> vec = new ArrayList<>();
for(int i=1;i<=(target-1)/2;i++){// (target-1)/2 等效于 target/2向下取整
// start i
int sum=0;
for(int j=i;;j++){
sum+=j;
if(sum>target){// say sum[3,4] and add 5, we get 12, but target=11 // so turn to try the next i as start
break;
}else if(sum==target){
int[] sub = new int[j-i+1];// 以i为开头的子数组,和为 target
for(int k=i;k<=j;k++){
sub[k-i]=k;
}
vec.add(sub);
// one start has only one solution, so let's try i as the next solution
break;
}
}
}
return vec.toArray(new int[vec.size()][]);
}
解法二:双指针
可以把l,r想象成滑动窗口的左右边界,
当窗口内的和等于target时,把当前窗口记录到答案中,然后l++,表示尝试下一个开头;
当窗口内的和大于target时,l++,表示当前的开头没有希望了,尝试下一个开头;
当窗口内的和小于target时,r++,表示扩大当前窗口。
public int[][] findContinuousSequence(int target){
List<int[]> vec = new ArrayList<>();
for(int l=1,r=2;l<=(target/2);){
int sum = (r-l+1)*(r+l)/2;
if(sum==target){
int[] res = new int[r-l+1];
for(int k=l;k<=r;k++){
res[k-l]=k;
}
vec.add(res);
l++;
}else if(sum>target){
l++;
}else{
r++;
}
}
return vec.toArray(new int[vec.size()][]);
}