剑指Offer——JZ41.和为S的连续正数序列【滑动窗口】【√N复杂度数学解法】

题目传送门


在这里插入图片描述


题解

  • 暴力直接gun粗
  • 对于连续区间的问题,前缀和滑动窗口都是不错的方案。
  • 滑动窗口的解法的时间复杂度很显然是 O ( N ) O(N) O(N),实际时间复杂的可以常数降低至 O ( N 2 ) O(\frac{N}{2}) O(2N)
  • 前缀的话,一遍求前缀数组,然后按照滑动窗口的方式。其实最终都是滑动窗口的思维方式,因为 O ( N 2 ) O(N^2) O(N2) 枚举起终点的解法太垃圾了
还有一种官方没有的解法,时间复杂度可以降低至 O ( N ) O(\sqrt{N}) O(N )
  • 如果长度为 n n n 的连续整数和为 s u m sum sum,那么 s u m n \frac{sum}{n} nsum 就是中位数。知道中位数和长度,就可以得到整个序列了
  • 可以对 n n n 进行分类讨论:
    n 是 奇 数 : n是奇数: n序列中间数正好是 s u m n \frac{sum}{n} nsum,开始元素为 s u m n − ( n − 1 2 ) \frac{sum}{n}-(\frac{n-1}{2}) nsum(2n1)。条件为:n % 2 == 1 && sum % n == 0;
    n 是 偶 数 : n是偶数: n序列偏左的中间数是 s u m n \frac{sum}{n} nsum,开始元素同上。条件为:(sum%n) * 2 == n
  • 那么n的范围是多少呢?最小肯定是2,最大的时候,我们就需要使整体和最小。那么久从1开始算起。根据等差数列和公式,得到条件: ( 1 + n ) ∗ n / 2 < = S (1+n)*n/2<=S (1+n)n/2<=S n < 2 S n<\sqrt{2S} n<2S
  • 但是注意,偶数的时候,一定会找到符合题意的么?
  • 例如:n=4的时候,sum=6。按照上述方式划分得到的是[0,1,2,3]不符合题意。因为我们只要正整数组合。
  • 或者是否会出现[-1,0,1,2…]这种情况呢?我们需要正确性证明。
  • 注意到我们所取的 n n n 的范围: [ 2 , 2 S ] [2, \sqrt{2S}] [2,2S ]
  • 在这个范围内的 n n n是通过等差数列和公式推导出来的,也就是说,都是满足等差数列前n项和的n。推算条件的数列最小元素是1.所以,在这个条件下,肯定不会出现非正整数的情况。

AC-Code

  • O ( N ) 解 法 O(N)解法 O(N)
class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int>> ans;
        int L = 1, R = 1;    // [L. R] 
        int num = 1;
        while(L <= sum / 2) {
            if(num == sum) {
                vector<int> vec;
                for(int i = L; i <= R; ++i) 
                    vec.push_back(i);
                ans.push_back(vec);
                num -= L;
                ++L;
            }
            else if(num < sum) {
                ++R;
                num += R;
            }
            else {
                num -= L;
                ++L;
            }
        }
        return ans;
    }
};
/*     x-n,...,x,...,x+n
      (2*n+1)*x==sum        */
  • O ( N ) 解 法 O(\sqrt{N})解法 O(N )
class Solution {
public:
    vector<vector<int> > FindContinuousSequence(int sum) {
        vector<vector<int>> ans;
        int n = sqrt(sum * 2);
        for(int i = n; i >= 2; --i) {
            if(i & 1 && sum % i == 0 || (sum % i) * 2 == i) {
                vector<int> vec;
                for(int j = 0, k = (sum / i) - (i - 1) / 2; j < i; ++j, ++k)
                    vec.push_back(k);
                ans.push_back(vec);
            }
        }
        return ans;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值