【剑指offer刷题日记 Java版】57 - II. 和为s的连续正数序列

404 阅读2分钟

题目

输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。

序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。

示例 1:

输入:target = 9 输出:[[2,3,4],[4,5]]

示例 2:

输入:target = 15 输出:[[1,2,3,4,5],[4,5,6],[7,8]]

解法一 滑动窗口

其实滑动窗口就是一直维护一个窗口,通过控制这个窗口的起始点和结束点的位置,来更改得到的值。比如这题,我们从最开始滑动,如果当前窗口内的值小于target,我们则把窗口的结束点向左移动,反之如果大于,则我们需要移除右边的,这样,我们的窗口会从队头扫描到队尾,把所有符合条件的连续数组都给记录下来。

这里最外部的while循环为 left<=target/2的原因是,因为最右边的数right最小的情况下也为left,那我们需要满足最极限的情况,即left+right = left + left <= target, 则可以得到left <= target/2。

class Solution {
    public int[][] findContinuousSequence(int target) {
        
        // 最开始的时候默认左边界和右边界均为1起始
        int left = 1;
        int right = 1;
        List<int[]> result = new ArrayList<>();
        // 一直储存着现在这个窗口内所有数字的和
        int sum = 0;

        while(left <= target /2){
            // 当现在的和小于目标时,我们需要把窗口向右边扩大一位
            if(sum < target){
                sum += right;
                right ++;
            // 当现在的和大于目标时,我们需要把窗口最左边一位的移除
            }else if(sum > target){
                sum -= left;
                left ++;
            // 当和等于目标时,我们把窗口的现在的数储存起来,同时把最左边的两个数移除
            }else{
                int[] temp = new int[right-left];
                for(int i = left; i < right; i++){
                    temp[i-left] = i;
                }
                result.add(temp);
                // 为什么要移除最左边的两个数呢,因为我们的目的是把整个窗口滑到最右边,
                // 下一步一定是向最右扩张,而此时我们的值已经等于target了,而最左边的
                // 第一位一定比向右扩张的小,最左边只移除一位的话,还是一定大于target,
                // 只有再移除最左边的第二位才能保证此时sum可能小于target
                sum -= left+left+1;
                left += 2;
            }
        }
        return result.toArray(new int[result.size()][]);
    }
}

时间复杂度:O(target) 空间复杂度:O(1)