【剑指offer】74.和为S的连续正数序列

总目录:

算法之旅导航目录

 

1.问题描述

小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列?

数据范围:0<n≤100
进阶:时间复杂度 O(n)

 

2.问题分析

 用等差数列的求和公式,只是差固定为1

1滑动窗口法

用一个窗口进行滑动,窗口内和小于目标值则右边界+1,窗口内和大于目标值则左边界+1,等于目标值时则把窗口内的序列保存下来然后左边界+1

2抓中间数法

假设目标值tgtVal可以分为n项之和,n取值范围为[2-tgtVal],tgtVal/n就是序列中间项的和,只需要区分以下n是奇数还是偶数即可得到该序列,通过等差数列求和公式计算序列和然后与目标值进行比较。


3.代码实例

滑动窗口法

 1 import java.util.ArrayList;
 2 public class Solution {
 3     public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
 4         //存放结果
 5         ArrayList<ArrayList<Integer> > result = new ArrayList<>();
 6         //两个起点,相当于动态窗口的两边,根据其窗口内的值的和来确定窗口的位置和大小
 7         int plow = 1,phigh = 2;
 8         while(phigh > plow){
 9             //由于是连续的,差为1的一个序列,那么求和公式是(a0+an)*n/2
10             int cur = (phigh + plow) * (phigh - plow + 1) / 2;
11             //相等,那么就将窗口范围的所有数添加进结果集
12             if(cur == sum){
13                 ArrayList<Integer> list = new ArrayList<>();
14                 for(int i=plow;i<=phigh;i++){
15                     list.add(i);
16                 }
17                 result.add(list);
18                 plow++;
19             //如果当前窗口内的值之和小于sum,那么右边窗口右移一下
20             }else if(cur < sum){
21                 phigh++;
22             }else{
23             //如果当前窗口内的值之和大于sum,那么左边窗口右移一下
24                 plow++;
25             }
26         }
27         return result;
28     }
29 }
View Code

抓中间数法

 1 class Solution {
 2   public:
 3     void addVec(vector<vector<int>>& vec, int start, int end) {
 4         if (start > end) {
 5             return;
 6         }
 7 
 8         vector<int> newVec;
 9         for (int i = start; i <= end; i++) {
10             newVec.push_back(i);
11         }
12 
13         vec.push_back(newVec);
14     }
15 
16     vector<vector<int>> FindContinuousSequence(int sum) {
17         vector<vector<int>> ret;
18         if (sum <= 0) {
19             return ret;
20         }
21 
22         //不算1等分的情况
23         //ret.push_back(vector<int>(1, sum));
24 
25         int midVal = 0;
26         int start = 0, end = 0;
27         //试图将sum分为i份
28         for (int i = sum; i > 1; i--) {
29             midVal = sum / i;
30 
31             //如果进行偶数等分
32             if ((i % 2) == 0) {
33                 //计算首尾
34                 start = midVal + 1 - i / 2;
35                 end = midVal + i / 2;
36                 if (start <= 0) {
37                     continue;
38                 }
39 
40                 //验证序列和
41                 if (((start + end)*i / 2) == sum) {
42                     addVec(ret, start, end);
43                 }
44             } else { //奇数等分midva
45                 //计算首尾
46                 start = midVal - i / 2;
47                 end = midVal + i / 2;
48                 if (start <= 0) {
49                     continue;
50                 }
51 
52                 //验证序列和
53                 if ((midVal * i) == sum) {
54                     addVec(ret, start, end);
55                 }
56             }
57         }
58 
59 
60         return ret;
61     }
62 };
View Code

 

posted @ 2022-12-06 20:11  啊原来是这样呀  阅读(46)  评论(0)    收藏  举报