zoukankan      html  css  js  c++  java
  • 寻找总和为n的连续子数列之算法分析

    看到有这么道算法题在博客园讨论,算法eaglet邀月都已经设计出来了,花了点时间读了下,学到点东西顺便记录下来吧。

    题目是从1...n的数列中,找出总和为n的连续子数列。

    这里先设好算法中需要用到的关键变量:

    • s:目标子数列的第一个元素
    • k:目标子数列的长度

    那么目标子数列可以表示为(s, k)

    1. naive算法(n^2)

    最笨的,但是最容易的想到的方法,就是穷举所有的子数列:

    for s = 1 to n
    for k = 1 to n-s+1
    if sum(s, k) == n
    output(s, k)

    复杂度为:n + (n-1) + (n-2) + (n-3).... = n(n-1)/2

    所以,其复杂度是O(n^2)

    2. 用二分法改进的naive算法 (nlog2n)

    我们需要充分利用输入的特性,这里,原始数列的一个很明显的特点就是有序,而利用有序数列提高效率的最常用方法就是二分法。这里我们可以注意到,针对某个子数列起始点s,我们没有必要逐个长度的去求和判断,而是利用其有序的性质,先求(s, (n+s)/2)的和。如果等于n则输出,如果大于n,则数列结尾在前半段,否则在后半段:

    复制代码
    for s = 1 to n
      low = s
      high = n
      while low < high
        mid = (low + high)/2
        sum = sum(s, mid)
        if sum == n
    output(s, mid)
        else if(sum > n)
    high = mid
        else
    low = mid
    复制代码

    很明显,此算法复杂度为O(nlog2n)

    3. 利用规律s*k <= n而设计的算法 (nlnn)

    我们知道,s是目标子数列的第一个元素,也是最小的元素,所以必然有sum(s,k) >= s*k, 也就是n>=s*k, 也就是k <= n/s,于是算法可以写成:

    for s = 1 to n
      for k = 1 to n/s
        if sum(s, k) == n
    output(s, k);

    此处,其复杂度并不是显而易见,但稍加分析:

    复杂度 = n + n/2 + n/3 + n/4 + ... + n/n = n (1 + 1/2 + 1/3 + 1/4 + .. + 1/n),可以注意到,括号中的部分是一个调和级数,其和为lnn。

    于是,此算法的复杂度为 O(nlnn),比算法2稍佳,因为lnn的底数要稍大些。

    4. 利用规律s*k = n-k(k-1)/2而设计的算法(sqrt(n))

    我们知道,对于子数列求和,其公式为:

    n = k(s+ (s+k-1))/2 = s*k + k(k-1)/2

    得出:

    s*k = n - k(k-1)/2

    由这个公式我们可以得到两点信息:

    • 1*k <= s*k = n-k(k-1)/2,推出n-k(k-1)/2 >= k
    • 如果n-k(k-1)/2能够整除k,则k是目标子数列的长度,而起始点可以由公式算出:s = (n-k(k-1)/2)/k

    于是,算法就可以以k为变量递增,以n-k(k-1)/2 >= k为限制条件:

    复制代码
    k = 1
    v = n-k(k-1)/2
    while v >= k
      if v % k == 0
    output(v/k, k) // 如果能整除,则找到解,并且起始点为v/k
      k++
      v = n-k(k-1)/2
    复制代码

    分析复杂度,我们只需关注k的变化,k是从1递增到某个数结束,关键是如何求这个截止的k。

    我们的循环结束条件是:

    n-k(k-1)/2 >= k

    化简得到:

    k^2 + k <= 2n

    k^2 <=  2n - k

    因为k > 0,于是有

    k^2 < 2n

    k < sqrt(2n)

    所以,这个截止的k就应该是sqrt(2n)或者略小于它

    到这里,就不难看出其算法复杂度为O(sqrt(n)) - 略去常数因子和低阶函数

  • 相关阅读:
    指定一个M3U8文件,判断它包含的TS文件是不是都存在。指定一个Office生成的Swf文件,判断它包含的Swf文件是不是完整都存在。
    东师理想云平台异步任务处理系统V2.0重构思路
    Mysql查询出所有列名
    IntelliJ IDEA添加jar包
    IDEA下利用Jrebel插件实现JFinal项目main方法【热加载】
    整理OpenResty+Mysql+Tomcat+JFinal+Cannal+HUI
    有没有 linux 命令可以获取我的公网 ip, 类似 ip138.com 上获取的 ip?
    MongoDB学习笔记(5)--document
    MongoDB学习笔记(4)--collection
    MongoDB学习笔记(3)--删除数据库
  • 原文地址:https://www.cnblogs.com/xumaojun/p/8543175.html
Copyright © 2011-2022 走看看