个人评价:使用暴力破解法,写出不难,但是如何继续优化还是挺值得深入思考的~

题目难度:🌟 (参考 LeeCode 简单🌟 、中等🌟 🌟 、困难🌟 🌟 🌟 )

另,通常算法题会考察分析时间复杂度和空间复杂度,可参考笔者之前的文章

「数据结构&算法」必知必会系列:3. 时间 & 空间复杂度(下)

和为s的连续正数序列

题目描述

剑指 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 <= target <= 10^5

解题思路

方法一

先拆解子问题:已知 和 以及 序列长度,可否获取正整数序列?思路如下:

  • 正整数等差数列,使用算术公式,首项 first,长度 len =》sum = (first+first+len-1)*len/2
  • 如果已知sum和,可直接返回正整数序列,如已知 9,长度 2 =》[4,5];长度 3 =》[2,3,4]

Go 代码如下:

func getNumList(target int, len int) []int {
    list := []int{}
    for i := 0; i < len; i++ { //i为序列index
        list = append(list, target/len-(len-1)/2+i)
    }
    return list
}

回到主问题,如何得知和可以匹配什么长度的序列?

暴力遍历:长度从 2 遍历到 target/len >(len-1)/2

遍历终点的条件,可理解为序列首位得 > 1

func findContinuousSequence(target int) [][]int {
    result := make([][]int, 0)
    for len := 2; target/len > (len-1)/2; len++ {
        s := [][]int{getNumList(target, len)}
        result = append(s, result...) //注意加到数列第一个,因为长度越长,首项越小
    }
    return result
}

分析复杂度?🙌 时间 O(n),额外空间复杂度 O(n)

那么,是否仍有优化空间?减少计算量以及减少空间

暴力穷举示例,其实可以发现,和得满足一定的条件才可被组成正整数序列,而奇数个和偶数个序列的规律又不大相同。这个类似数学找规律,得出如下附加条件:

  • 奇数个正整数序列:满足和可整除长度(理解为 中位数*个数 = 总和)
  • 偶数个正整数序列:满足和×2整除长度 且 和×2除长度之后得为奇数(偶数列为对称值,递归为最小两个数,和必为奇数)

于是,我们可以增加一些条件,优化算法:

func findContinuousSequence(target int) [][]int {
    result := make([][]int, 0)
    for len := 2; target/len > (len-1)/2; len++ {
        if (len%2 == 1) && target%len == 0 { //奇数个正整数序列
            s := [][]int{getNumList(target, len)}
            result = append(s, result...)
        }
        if (len%2 == 0) && target*2%len == 0 && target*2/len%2 == 1 { //偶数个正整数序列
            s := [][]int{getNumList(target, len)}
            result = append(s, result...)
        }
    }
    return result
}

https://goplay.tools/snippet/WxIJlDSfIjI Go play~

方法二

数学公式,一元二次方程法,有兴趣的自己尝试下 Coding 吧~

总结

使用暴力破解法,写出不难,但是效率低,算法是需要优化的~

程序员还是需要数学基础的,使用数学,不少算法仍有继续优化的空间 ヾ(◍°∇°◍)ノ゙