【leetcode】57 - II. 和爲s的連續正數序列

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)。

注:這道題當前能夠用等差數列的求和公式來計算滑動窗口的和。不過我這裏沒有使用求和公式,是爲了展現更通用的解題思路。實際上,把題目中的正整數序列換成任意的遞增整數序列,這個方法均可以解。

leetcode滑動窗口詳解

解題代碼:

/**
 * @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
};

解題代碼:

執行用時:

圖片.png

相關文章
相關標籤/搜索