[LeetCode](面试题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 <= target <= 10^5

解题思路

解法一:数学 + 间隔法

设数列第一项为t,项数为n,则由等差数列求和公式 [t+(t+n-1)]*n/2 = s,
化简得 t = [s-n(n-1)/2]/n,因为t为正整数,所以 s>n(n-1)/2,[s-n(n-1)/2] % n = 0。又项数至少为2,所以n>=2。
步骤:
1)初始化项数 n=2,结果数列res = [];
2)进行循环,当 n(n-1)/2 < s时,即 while(n(n-1)/2 < s):
2.1)判断[s-n(n-1)/2]能否整除n,即[s-n(n-1)/2] % n = 0,是则进行如下操作:求出 t,并根据当前项数n求出整个数列,加入结果数列中。
2.2)否则项数加1,继续循环;
3)由于项数是从小到大,所以数列是以从大到小的顺序存入结果数列的,最后需要进行反转操作。

解法二:滑动窗口(双指针)

主要步骤:
1)初始化:双指针i=1, j=2,当前和为 cursum=i+j。
2)循环搜索:当指针i大于 target/2 时跳出循环。
2.1)当窗口的和小于 target 的时候,窗口的和需要增加,所以要扩大窗口,窗口的右边界向右移动;
2.2)当窗口的和大于 target 的时候,窗口的和需要减少,所以要缩小窗口,窗口的左边界向右移动;
2.3)当窗口的和恰好等于 target 的时候,记录此时的结果,并将指针i右移一位(或者指针j右移一位)。

序列至少要包括两个数字,所以指针i最多增加到 target / 2,因为(target/2) + (target/2)+1>=target。

代码

解法一:数学 + 间隔法

Python代码如下:

class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        #初始化项数为2
        n = 2
        res = []
        while n*(n-1)/2 < target: # 必须为正数
            if (target - n*(n-1)/2) % n == 0: #判断是否能够整除
                t = int((target - n*(n-1)/2) / n) # float转int
                res.append(list(range(t, t+n)))
            n += 1
        # 数列反转
        return res[::-1]

解法二:滑动窗口(双指针)

Python代码如下:

class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        i = 1
        j = 2
        cursum = i+j
        res = []
        while i <= target//2:
            if cursum == target:
                res.append(list(range(i, j+1)))
                # j += 1
                # cursum += j
                # 或者
                cursum -= i
                i += 1
            elif cursum < target:
                j += 1
                cursum += j
            else:
                cursum -= i
                i += 1
        return res

Java代码如下:

class Solution {
    public int[][] findContinuousSequence(int target) {
        int i = 1;
        int j = 2;
        int cursum = i+j;
        List<int[]> res = new ArrayList<>();
        while(i<=target/2){
            if(cursum == target){
                int[] arr = new int[j-i+1];
                for(int k=i; k<=j; k++){
                    arr[k-i] = k;
                }
                res.add(arr);
                // j++;
                // cursum += j;
                // 或者
                cursum -= i;
                i++;
            }else if(cursum < target){
                j++;
                cursum += j;
            }else{
                cursum -= i;
                i++;
            }
        }
        return res.toArray(new int[res.size()][]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值