算法实现--求和为s的连续正数序列.

求和为s的连续正数序列.

如,输入9,输出序列:{2,3,4}、和{4,5}

下面是暴力循环法:

// 暴力法
vector<vector<int>> FindContinuousSequence(int target) {
	int sum = 0;
	vector<vector<int>> result;
	for (int i = 1; i <= target / 2; ++i)
	{
		for (int j = i; j < target; ++j)
		{
			sum += j;
			if (sum == target)
			{
				vector<int> temp;
				for (int k = i; k <= j; ++k)
				{
					temp.push_back(k);
				}
				result.push_back(temp);
				break;
			}
			else if (sum > target)
				break;
		}
		sum = 0;
	}
	return result;
}

 

下面是双指针法,也就是滑动窗口法:时间复杂度O(n)

滑动窗口的重要性质是:窗口的左边界和右边界永远只能向右移动,而不能向左移动。这是为了保证滑动窗口的时间复杂度是 O(n)。如果左右边界向左移动的话,这叫做“回溯”,算法的时间复杂度就可能不止 O(n)。

要用滑动窗口解这道题,我们要回答两个问题:

第一个问题,窗口何时扩大,何时缩小?
第二个问题,滑动窗口能找到全部的解吗?
对于第一个问题,回答非常简单:

当窗口的和小于 target 的时候,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动
当窗口的和大于 target 的时候,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动
当窗口的和恰好等于 target 的时候,我们需要记录此时的结果。设此时的窗口为 [i, j)[i,j),那么我们已经找到了一个 ii 开头的序列,也是唯一一个 ii 开头的序列,接下来需要找 i+1i+1 开头的序列,所以窗口的左边界要向右移动

// 双指针法
vector<vector<int>> findContinuousSequence(int target) {
	int i = 1; // 右指针
	int j = 1; // 左指针
	int sum = 0;
	vector<vector<int>> result;
	while (i <= target / 2) // 考虑边界,i可能为target/2,如9/2=4
	{
		if (sum < target)
		{
			sum += j++;
		}
		else if (sum > target)
		{
			sum -= i++;
		}
		else
		{
			vector<int> temp;
			// 能进到这里,上一次肯定是j++了,所以k<j为结束条件
			for (int k = i; k < j; ++k)
			{
				temp.push_back(k);
			}
			result.push_back(temp);

			sum -= i++; // 注意要右移i
		}
	}
	return result;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章