[劍指 offer] --滑動窗口-- 面試題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

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

2 解題思路

  • 方法:滑動窗口
    什麼是滑動窗口
    滑動窗口可以看成數組中框起來的一個部分。在一些數組類題目中,我們可以用滑動窗口來觀察可能的候選結果。當滑動窗口從數組的左邊滑到了右邊,我們就可以從所有的候選結果中找到最優的結果。

對於這道題來說,數組就是正整數序列 [1,2,3,…,n]。我們設滑動窗口的左邊界爲 i,右邊界爲 j,則滑動窗口框起來的是一個左閉右開區間 [i,j)。注意,爲了編程的方便,滑動窗口一般表示成一個左閉右開區間。在一開始,i=1,j=1,滑動窗口位於序列的最左側,窗口大小爲零。
在這裏插入圖片描述
如何用滑動窗口解這道題
要用滑動窗口解這道題,我們要回答兩個問題:

第一個問題,窗口何時擴大,何時縮小?
第二個問題,滑動窗口能找到全部的解嗎?

對於第一個問題,回答非常簡單:

  • 當窗口的和小於 target 的時候,窗口的和需要增加,所以要擴大窗口,窗口的右邊界向右移動
  • 當窗口的和大於 target 的時候,窗口的和需要減少,所以要縮小窗口,窗口的左邊界向右移動
  • 當窗口的和恰好等於 target 的時候,我們需要記錄此時的結果。設此時的窗口爲 [i,j),那麼我們已經找到了一個 i 開頭的序列,也是唯一一個 i 開頭的序列,接下來需要找 i+1 開頭的序列,所以窗口的左邊界要向右移動
    對於第二個問題,我們可以稍微簡單地證明一下:
    在這裏插入圖片描述
    我們一開始要找的是 1 開頭的序列,只要窗口的和小於 target,窗口的右邊界會一直向右移動。假設 1+2+⋯+8 小於 target,再加上一個 9 之後, 發現 1+2+⋯+8+9 又大於 target 了。這說明 1 開頭的序列找不到解。此時滑動窗口的最右元素是 9。

接下來,我們需要找 2 開頭的序列,我們發現,2+⋯+8<1+2+⋯+8<target。這說明 2 開頭的序列至少要加到 9。那麼,我們只需要把原先 1~9 的滑動窗口的左邊界向右移動,變成 2~9 的滑動窗口,然後繼續尋找。而右邊界完全不需要向左移動。

以此類推,滑動窗口的左右邊界都不需要向左移動,所以這道題用滑動窗口一定可以得到所有的解。時間複雜度是 O(n)。
作者:nettee
鏈接:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/solution/shi-yao-shi-hua-dong-chuang-kou-yi-ji-ru-he-yong-h/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

3 解決代碼

  • 方法:滑動窗口《java代碼》
class Solution {
    public int[][] findContinuousSequence(int target) {
        int i = 1;
        int j = 1;
        int sum = 0;
        List<int[]> res = new ArrayList<>();
        while(i <= target/2){
            if(sum < target){
                // 右邊界向右移動
                sum += j;
                j++;
            }
            else if(sum > target){
                // 左邊界向右移動
                sum -= i;
                i++;
            }
            else{
                // 記錄結果
                int[] arr = new int[j - i];
                //對應位置打印,k-i是0,放i,爲1,放i+1,依次遞增
                for(int k = i; k <j; k++){
                    arr[k-i] = k;
                }
                res.add(arr);
                // 左邊界向右移動
                sum -= i;
                i++;
            }
        }
        //最後輸出的是二維數組
        return res.toArray(new int[res.size()][]);

    }
}
  • 方法:滑動窗口《python3代碼》
class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        i = 1
        j = 1
        sum = 0
        res = []
        while i <= target//2:
            if sum < target:
                sum += j
                j += 1
            elif sum > target:
                sum -= i
                i += 1
            else:
                arr = list(range(i, j))
                res.append(arr)
                sum -= i
                i += 1
        return res
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章