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

该博客主要讨论了LeetCode的一道题目,关于找到和为s的连续正数序列。博主首先反思了暴力枚举的不足,然后提出使用双指针滑动窗口的方法降低时间复杂度到O(n)。接着,博主指出所求序列是等差数列,并介绍了如何利用一元二次方程求根公式直接计算,优化了空间复杂度。博客中还提到了避免溢出的计算策略,并强调了解的正数性质。

题目链接:leetcode.

没把题看清楚就做,(但终于一次写出一个回溯了!!!
要求连续的正整数序列,回溯得到的根本不连续鸭

class Solution {
public:
	vector<vector<int>> ans;
	vector<int> tmp;
	void dfs(int target, int sum, int index)
	{
		if(sum > target)
		{
			return;
		}
		if(sum == target)
		{
			ans.push_back(tmp);
			return;
		}
		while(index < target)
		{
			tmp.push_back(index);
			dfs(target, sum + index, index + 1);
			tmp.pop_back();
			index++; 
		}
	}
    vector<vector<int>> findContinuousSequence(int target) {
        dfs(target, 0, 1);
        return ans;
    }
};

一、暴力枚举:

/*
执行用时:316 ms, 在所有 C++ 提交中击败了7.27%的用户
内存消耗:96 MB, 在所有 C++ 提交中击败了5.30%的用户
*/
class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        if(target < 3)
        	return {};
		vector<vector<int>> ans;
		for(int i = 1;i + i + 1 < target;++i)
		{
			vector<int> tmp;
			tmp.push_back(i);
			tmp.push_back(i + 1);
			int t = i + i + 1;
			for(int j = i + 2;t < target;++j)
			{
				t += j;
				tmp.push_back(j);
				if(t == target)
				{
					ans.push_back(tmp);
					break;
				}
			}
		}
		return ans;
    }
};

很好,我很蠢,竟然只想到了暴力,没有利用上题中的条件
二、连续也一定有序,双指针滑动窗口,时间复杂度只有O(n)

/*
执行用时:4 ms, 在所有 C++ 提交中击败了64.50%的用户
内存消耗:6.4 MB, 在所有 C++ 提交中击败了94.39%的用户
*/
class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        vector<vector<int>> ans;
        int limit = target / 2 + 1;
        int i = 1, j = 2;
        int windows = i + j;
        while(i < j && j <= limit)
        {
        	if(windows == target)
        	{
        		vector<int> tmp;
        		for(int k = i;k <= j;++k)
        		{
        			tmp.emplace_back(k);
        		}
        		ans.push_back(tmp);
        		windows -= i;
        		i++;
        	}
        	else if(windows < target)
        	{
        		j++;
        		windows += j;
        	}
        	else
        	{
        		windows -= i;
        		i++;
        	}
        }
        return ans;
    }
};

第三种方法,所求子数列是一个等差数列,可以用数学方法直接得出其和sum = (i + j) * (j - i + 1) / 2;
sum == target时,把子数列记录起来,其实是对第一种方法的优化,O(n),O(1)

对于每一个i,不必循环计数求有没有满足条件的j,而是直接用一元二次求根公式算出满足条件的j,看其是否存在(是否为整数)
j^2 + j + (i - i^2 - 2 * target) = 0;
因为x = (-b ±√(b^2 - 4 * b * c) ) / (2 * a);
故解 j = (-1 ±√(1 - 4 * (i - i^2 - 2 * target)) ) / 2;
j一定为正数,所以不取负解
可以通过计算出double类型的j,比较 j == (int)j 判断是否为整数
也可以通过观察,发现判别式 Δ= (b^2 - 4 * b * c)开根号一定是整数 ,且-1 + √Δ一定是偶数
另外:求取i^2过程中可能会导致溢出,所以要使用long

/*
执行用时:8 ms, 在所有 C++ 提交中击败了28.48%的用户
内存消耗:6.6 MB, 在所有 C++ 提交中击败了57.78%的用户
*/
class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        vector<vector<int>> ans;
        int limit = target / 2 + 1;
        for(long i = 1;i < target;++i)
		{
			double j = (-1 + sqrt(1 - 4 * (i * (1 - i) - 2 * target)) ) / 2;
			if(j == (int) j)
			{
				vector<int> tmp;
				for(int k = i;k <= j;++k)
				{
					tmp.emplace_back(k);
				}
				ans.emplace_back(tmp);
			}
		} 
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值