问题:
1. 给定一个数组其每个元素都是正数,和一个给定值M,求所有连续的子数组其和等于M。
2. 给定一个正数N,求所有和为N的连续正数序列。
这类求子数组之和的问题与编程之美2.12——快速寻找满足条件的两个数或三个数问题的求解思路相似,都是可用双指针法在O(n)的时间内解决。
1.解法:
我们让两个指针i,j都从数组的第一个元素开始,记变量sum初始化为第一个元素的值,sum表示子数组[i,j]的元素之和。在每步操作中,若sum的值小于等于M,则j增加1,并且让sum加上元素A[j];若sum的值大于M,则先让sum减去元素A[i],再i增加1。
- // 求连续子序列和等于m
- void FindSumK(int *A, int n, unsigned int m)
- {
- int i, j;
- i = j = 0;
- int sum = A[0]; // 表示子数组[i,j]的元素之和
- // 当j==n时,说明当前子数组之和sum小于m,而增加i只会让sum更加小于m,
- // 所以不用使i再增加到n
- while (i<=j && j<n)
- {
- // sum大于m,增加i,以减小sum
- if (sum >= m)
- {
- if (sum == m) printf("%d-%d\n",i,j);
- sum -= A[i];
- ++i;
- }
- // sum小于m,增加j,以增大sum
- else
- {
- ++j;
- sum += A[j];
- }
- }
- }
2.解法:
让我们先举个例子,例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续的序列1-5,4-6和7-8。
同样是计算子数组之和,同样使用双指针法,具体分析同上。唯一需要注意下它的边界条件,由于子数组至少要有两个数字,所以较小的指针的位置遍历到n/2就结束。
- // 求所有和为n的连续正数序列
- void FindSumN(int n)
- {
- int i, j;
- i = j = 1;
- int sum = 1;
- while(i<=n/2)
- {
- if (sum <= n)
- {
- if (sum == n) printf("%d-%d\n",i,j);
- ++j;
- sum += j;
- }
- else
- {
- sum -= i;
- ++i;
- }
- }