1.輸入一個正整數 target ,輸出全部和爲 target 的連續正整數序列(至少含有兩個數)。
序列內的數字由小到大排列,不一樣序列按照首個數字從小到大排列。算法
示例 1: 輸入:target = 9 輸出:[[2,3,4],[4,5]]
示例 2: 輸入:target = 15 輸出:[[1,2,3,4,5],[4,5,6],[7,8]]
限制:編程
1 <= target <= 10^5
2.解題思路:數組
利用滑動窗口: 滑動窗口能夠當作數組中框起來的一個部分。在一些數組類題目中,咱們能夠用滑動窗口來觀察可能的候選結果。當滑動窗口從數組的左邊滑到了右邊,咱們就能夠從全部的候選結果中找到最優的結果。url
對於這道題來講,數組就是正整數序列 [1,2,3,…,n][1, 2, 3, \dots, n][1,2,3,…,n]。咱們設滑動窗口的左邊界爲 iii,右邊界爲 jjj,則滑動窗口框起來的是一個左閉右開區間 [i,j)[i, j)[i,j)。注意,爲了編程的方便,滑動窗口通常表示成一個左閉右開區間。在一開始,i=1,j=1i=1, j=1i=1,j=1,滑動窗口位於序列的最左側,窗口大小爲零。.net
滑動窗口的重要性質是:窗口的左邊界和右邊界永遠只能向右移動,而不能向左移動。這是爲了保證滑動窗口的時間複雜度是 O(n)O(n)O(n)。若是左右邊界向左移動的話,這叫作「回溯」,算法的時間複雜度就可能不止 O(n)O(n)O(n)。3d
在這道題中,咱們關注的是滑動窗口中全部數的和。當滑動窗口的右邊界向右移動時,也就是 j = j + 1,窗口中多了一個數字 j,窗口的和也就要加上 j。當滑動窗口的左邊界向右移動時,也就是 i = i + 1,窗口中少了一個數字 i,窗口的和也就要減去 i。滑動窗口只有 右邊界向右移動(擴大窗口) 和 左邊界向右移動(縮小窗口) 兩個操做,因此實際上很是簡單。 如何用滑動窗口解這道題code
要用滑動窗口解這道題,咱們要回答兩個問題:blog
1.第一個問題,窗口什麼時候擴大,什麼時候縮小? 2.第二個問題,滑動窗口能找到所有的解嗎?圖片
對於第一個問題,回答很是簡單:leetcode
1.當窗口的和小於 target 的時候,窗口的和須要增長,因此要擴大窗口,窗口的右邊界向右移動 2.當窗口的和大於 target 的時候,窗口的和須要減小,因此要縮小窗口,窗口的左邊界向右移動 當窗口的和剛好等於 target 的時候,咱們須要記錄此時的結果。設此時的窗口爲 [i,j)[i, j)[i,j),那麼咱們已經找到了一個 iii 開頭的序列,也是惟一一個 iii 開頭的序列,接下來須要找 i+1i+1i+1 開頭的序列,因此窗口的左邊界要向右移動
對於第二個問題,咱們能夠稍微簡單地證實一下:
咱們一開始要找的是 1 開頭的序列,只要窗口的和小於 target,窗口的右邊界會一直向右移動。假設 1+2+⋯+81+2+\dots+81+2+⋯+8 小於 target,再加上一個 9 以後, 發現 1+2+⋯+8+91+2+\dots+8+91+2+⋯+8+9 又大於 target 了。這說明 1 開頭的序列找不到解。此時滑動窗口的最右元素是 9。
接下來,咱們須要找 2 開頭的序列,咱們發現,2+⋯+8<1+2+⋯+8<target2 + \dots + 8 < 1 + 2 + \dots + 8 < \mathrm{target}2+⋯+8<1+2+⋯+8<target。這說明 2 開頭的序列至少要加到 9。那麼,咱們只須要把原先 1~9 的滑動窗口的左邊界向右移動,變成 2~9 的滑動窗口,而後繼續尋找。而右邊界徹底不須要向左移動。
以此類推,滑動窗口的左右邊界都不須要向左移動,因此這道題用滑動窗口必定能夠獲得全部的解。時間複雜度是 O(n)O(n)O(n)。
注:這道題當前能夠用等差數列的求和公式來計算滑動窗口的和。不過我這裏沒有使用求和公式,是爲了展現更通用的解題思路。實際上,把題目中的正整數序列換成任意的遞增整數序列,這個方法均可以解。
解題代碼:
/** * @param {number} target * @return {number[][]} */ var findContinuousSequence = function(target) { let i = 1; //滑動窗口的左邊界 let j = 1; //滑動窗口的右邊界 let sum = 0; //滑動窗口中數字的和 let res = []; //存放結果 while (i <= target / 2) { if (sum < target) { // 右邊界向右移動 sum += j; j++; } else if (sum > target) { // 左邊界向右移動 sum -= i; i++; } else { let temp = []; //存放結果 for (var k = i; k < j; k++) { temp.push(k); } res.push(temp); // 左邊界向右移動 sum -= i; i++; } } return res };
解題代碼:
執行用時: