【力扣日记】面试题57 - II. 和为s的连续正数序列

题目描述

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

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

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

输入:100
输出:[[9,10,11,12,13,14,15,16],[18,19,20,21,22]]

算法思路

NAIVE

第一个想到的方法是做一个字典,保存每个数的前n项和。
然后用双指针i,j遍历字典,将所有d[j]-d[i]==target的从i到j的连续数列保存到结果中。

这个双指针也可以当作滑动窗口来着没差啦。

class Solution:
    def findContinuousSequence(self, target: int):
        n,di,res=0,{0:0},[]
        for i in range(1,target//2+2):
            n+=i
            di[i]=n
        i,j=0,1
        while j<=target//2+1:
            v=di[j]-di[i]
            if v==target:
                ls=list(range(i+1,j+1))
                res+=[ls]
                j+=1
            if v>target:i+=1
            elif v<target:j+=1
        return res

执行用时 :188 ms, 在所有 Python3 提交中击败了63.02%的用户
内存消耗 :21.1 MB, 在所有 Python3 提交中击败了100.00%的用户

拓展

采用求和公式替代了字典,结果内存消耗显著减少,但是耗时显著增长。
考虑应该是用求和公式在While循环中出现了大量重复计算。

class Solution:
    def findContinuousSequence(self, target: int):
        n,res,i,j=0,[],0,1
        def SUM(n):return n*(n+1)/2
        while j<=target//2+1:
            v=SUM(j)-SUM(i)
            if v==target:
                res.append(list(range(i+1,j+1)))
                j+=1
            if v>target:i+=1
            elif v<target:j+=1
        return res

执行用时 :340 ms, 在所有 Python3 提交中击败了34.47%的用户
内存消耗 :13.4 MB, 在所有 Python3 提交中击败了100.00%的用户

然后添加字典,一边使用求和公式一边记录所有求过的和。
结果反向喜人。
耗时没咋变化,空间复杂度回到了最开始的水平。
幽灵飘。

class Solution:
    def findContinuousSequence(self, target: int):
        n,res,i,j,d=0,[],0,1,{}
        def SUM(n):return n*(n+1)/2
        while j<=target//2+1:
            v=d.setdefault(j,SUM(j))-d.setdefault(i,SUM(i))
            if v==target:
                res.append(list(range(i+1,j+1)))
                j+=1
            if v>target:i+=1
            elif v<target:j+=1
        return res

执行用时 :424 ms, 在所有 Python3 提交中击败了28.55%的用户
内存消耗 :21.1 MB, 在所有 Python3 提交中击败了100.00%的用户

IMPROVE

评论区还是多大佬啊,一看果然有好用的数学方法。

依然是求和公式:(首项+末项)*数列长度/2

  1. (n+1)*n/2==target ——>n=int((2*target)**(0.5))得到最长的数列长度
  2. 剪枝:所有数列长度都要满足条件(target*2) % len == 0
  3. 根据数学公式可以得到beg=((2*target)/len-len+1)/2;且beg必须是整数

如此,易得算法:

class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        res=[]
        for i in range(int((2*target)**(0.5)),1,-1):
            if (target*2) % i == 0:
                beg=((2*target)/i-i+1)/2
                if beg==int(beg):
                    beg=int(beg)
                    res.append(list(range(beg,beg+i)))
        return res

执行用时 :32 ms, 在所有 Python3 提交中击败了99.46%的用户
内存消耗 :13.3 MB, 在所有 Python3 提交中击败了100.00%的用户

妈耶 我到底是在写算法题还是数学题呢?沉思。

发布了210 篇原创文章 · 获赞 20 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Heart_for_Ling/article/details/104695034