剑指offer 57 - II. 和为s的连续正数序列

剑指 Offer 57 - II. 和为s的连续正数序列 - 力扣(LeetCode) (leetcode-cn.com)

目录

解法1:数学手段

运行结果

分析

确定可能的元素个数

根据等差数列所有项之和与项数的关系来筛选

 代码

解法2:滑动窗口

运行结果

分析

代码


解法1:数学手段

运行结果

剑指offer 57 - II. 和为s的连续正数序列_第1张图片

分析

确定可能的元素个数

满足条件的序列最少要有两个元素,最多可能有几个元素?

如果n个连续正数之和为s,设第一个数为x,即:

        x, x + 1, ..., x + n-1

这个序列之和:n * x + n * (n - 1) / 2 == s

我们需要求的是n的最大值,那么x需要取得最小值,x至少是1,所以令x = 1代入上式,得到:

n * (n + 1) == 2 * s

又因为:n ^ 2 < n * (n + 1) == 2 * s

所以:n < sqrt ( 2 * s )

则满足要求的序列至多有sqrt ( 2 * s )个元素,我们记为N

根据等差数列所有项之和与项数的关系来筛选

对于任意n ∈ [ 2, N ]个连续正数组成的,和为s的序列,其平均值为 s / n,第一个元素:

x = s / n - (n-1) / 2 (可以验证:x * n + n * (n - 1)/ 2 == s)

如果n为奇数,那么均值s / n必然为该序列的中值,且n必然整除s,即:

s % n == 0

如果n为偶数,那么均值s / n必然为该序列中间两个数的均值

设中间两个数为a, a+1
则:
s / n = (a + a+1) / 2
s / n = a + 1/2
s = a * n + n / 2

所以:s % n == n / 2

 代码

class Solution {
public:
	bool check(int s, int n) {
		return ((n & 1) && !(s % n)) ||
			(!(n & 1) && (s % n == n / 2));
	}

	vector> findContinuousSequence(int s) {
		int N = sqrt(2 * s);
		vector> res;

		for (int i = N; i >= 2; --i) {
			if (check(s, i)) {
				int a1 = s / i - (i - 1) / 2;
				vector arr(i);
				for (int j = 0; j < i; ++j) {
					arr[j] = a1 + j;
				}
				res.push_back(arr);
			}
		}

		return res;
	}
};

解法2:滑动窗口

运行结果

剑指offer 57 - II. 和为s的连续正数序列_第2张图片

分析

我们考虑检查所有可能的连续整数序列:

从1开始的连续整数序列,从2开始的连续整数序列,……,从s/2开始的连续整数序列

由于这些序列之间存在大量重复的元素,我们希望通过找出他们之间的关系来避免重复的求和运算。

设f(i)表示从i开始的、和小于s的连续整数序列的最后一项,s[i, f(i)]表示这个闭区间上所有整数之和。

则有:

s[i, f(i)] = i + i+1 + ... + f(i) < s
s[i, f(i)+1] = i + i+1 + ... + f(i) + f(i)+1 >= s

同理:

s[i+1, f(i+1)] = i+1 + i+2 + ... + f(i+1) < s
s[i+1, f(i+1)+1] = i+1 + i+2 + ... + f(i+1) + f(i+1)+1 >= s

由此我们可以找出f(i+1)与f(i)的关系:

由 s[i+1, f(i)] < s[i, f(i)] < s
得:
    f(i+1) >= f(i)

由
s[i+1, f(i)+2] = s[i, f(i)+1] + f(i)+2 - i
s[i, f(i)+1] >= s
f(i)+2 - i > 0
得:
    s[i+1, f(i)+2] >= s
则: f(i+1)+1 <= f(i)+2
即: f(i+1) <= f(i)+1

根据以上关系,我们得到:

f(i) <= f(i+1) <= f(i) + 1 <= f(i+1) + 1

因此当检验完s[i, f(i)+1]之后,我们只需检验s[i+1, f(i)+1],用s[i, f(i)+1] - i 即可。

代码

class Solution {
public:
	vector> findContinuousSequence(int s) {
		vector> res;
		int end = 1;
		int sum = 1;
		for (int beg = 1; beg <= s / 2; ++beg) {
			while (sum < s) {
				++end;
				sum += end;
			}
			if (sum == s) {
				int size = end - beg + 1;
				vector arr(size);
				for (int i = 0; i != size; ++i) arr[i] = beg + i;
				res.push_back(arr);
			}
			sum -= beg;
		}
		return res;
	}
};

你可能感兴趣的:(剑指offer-C++,算法,剑指offer,滑动窗口)