求和爲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;
}