剑指 Offer 57 - II. 和为s的连续正数序列

题目描述:

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

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

示例1:

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

示例2:

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

解题思路:

1、滑动窗口:

      设滑动窗口(i,j)的左边界为i,右边界为j,窗口内数据之和为sum

滑动窗口的性质

     (1)滑动窗口一般为一个左闭右开区间[i,j),即保存的数据是nums[i,j - 1](不包括nums[j]);

     (2)滑动窗口只有右边界向右移动(扩大窗口)左边界向右移动(缩小窗口) 两个操作。

找到连续正整数的过程:

     (1)当sum < target 时,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动,即j++,同时更新窗口的和,即sum += j

     (2)当sum > target 时,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动,即i++,同时更新窗口的和,即sum -= i

     (3)当sum == target 时,记录窗口中的数据,同时窗口的左边界向右移动(因为以左边界i为开头的序列已经没有了),即i++,同时更新窗口的和,即sum -= i

     当窗口的左边界大于target的一半时,说明连续子序列已经遍历完了,这是因为两个以上的大于target / 2的数之和肯定大于target

注:sum == target 时,记录窗口中的数据后,更新左边界时可以将左边界i向右移动两步,即i += 2,同时更新sum,即sum -= 2 * i + 1,这是因为当存在以i开头的子序列时,以i + 1开头的子序列肯定不符合条件。

2、等差数列:

     我们知道,等差数列的求和公式为sum = n * a + n(n - 1)d / 2(其中a为首项,d为公差,n为项数),在这道题中,由于是连续子序列,因此公差为1,因此求和公式可以表示为target = n * a + n(n - 1) / 2,从而得出a = (target - n * (n - 1) / 2) / n

     由于题目要求子序列中至少要有两个数字,因此n >= 2。所以我们可以从n = 2开始遍历,根据公式算出a的值,如果a是正整数,说明以a开头的连续子序列之和为target,如果a不是正整数,n继续遍历,直到 a <= 0时遍历结束。(因为随着n递增,a递减)

注: 这样算出来的子序列的长度是递增的,因此最后需要用Collections.reverse(res)将子序列存放的顺序反转,这样才符合题目的提交要求。

实现代码:

	 //解法1:滑动窗口
class Solution {
    public int[][] findContinuousSequence(int target) {
        int left = 1, right = 2;
        int sum = 3;
        List<int[]> res = new ArrayList<>();
        while(left <= target / 2) {
            if(sum < target) {
                right++;
                sum += right;
            }else if(sum > target) {
                sum -= left;
                left++;
            }else {
                int[] list = new int[right - left + 1];
                for(int i = left; i <= right; i++) {
                    list[i - left] = i;
                }
                res.add(list);
                sum -= 2 * left + 1;
                left += 2;
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}

    //解法2:等差数列求和公式: target = n * a + n(n - 1)d / 2(其中d=1)
    public int[][] findContinuousSequence1(int target) {
        int n = 2;
        ArrayList<int[]> res = new ArrayList<>();
        while (true){
            int temp = target - n * (n - 1) / 2;
            if(temp < n)//a <= 0
                break;
            if(temp % n == 0){
                int a = temp / n;
                int[] arr = new int[n];
                for(int i = 0; i < n; i++)
                    arr[i] = i + a;
                res.add(arr);
            }
            n++;
        }
        Collections.reverse(res);
        return res.toArray(new int[res.size()][]);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值