你好,我是goldsunC

让我们一起进步吧!

这篇文章因为写的时候使用的markdown,公式用的LaTex,然后转换的时候LaTex都转换成了好多图片,大家可以看到文章中有好多公式图片,然后复制的时候部分复制不上,不过不影响观看,感谢支持!

基本知识

当我们学数据结构与算法的时候,最基础的就是得会估计算法的时间复杂度大O,如果不会这个我们就没法对比算法的优劣了。对算法的时间复杂度是有数学定义的,其数学定义如下:如果存在正常数c和 使得当 ,则记为 。例如我们假定 ,显然如果当N较小时,前者比后者更大,但是后者会以更快的速度增长,因此若N较大时,后者将会比前者更大。这个转折点就是 。对比定义我们可以发现如果 时, ,当然取值还有很多,比如可以 ,都可以满足前者小于小于等于后者的要求,因此我们可以说。你可能会觉得稍微有点问题,1000N不是等于O(N)吗?是的,不过说成 没有错,这里只是举个例子,只是不那么严密而已,不过要注意的是,我们在实际使用中应保证尽可能的严密,那才是最好的答案,不然说了跟没说一样。当我们在说 时,是说保证函数T(N)在以不快于f(N)的速度增长;因此f(N)是T(N)的上界,T(N)是f(N)的下界。我们在对比函数时,往往对比的是增长速率。对时间复杂度定义,我们可以推导出两条结论:

  • 如果 ,那么:

  • 对任意常数k,。它说明对数增长得非常缓慢。

这些信息已经足够按照增长率对大部分常见的函数进行分类,我们可以简单得到一个函数增长率的关系:

通常,将常数或者低阶项放进大O是非常不好的习惯。不要说。在这两种情况下,正确的形式应该是。简单来说就是在需要大O表示时,各种简化都是可能发生的。低阶项一般可以被忽略,常数也可以被忽略。要求的精度是比较低的。我们可以通过 来确定两个函数的相对增长率,必要的时候还可使用使用洛必达法则。使用这种方法几乎总能够计算出两个函数的相对增长率,不过有时会有些复杂化。有时候不需要求极限便可以通过简单的代数方法求得结果。

比如对比 谁的增长率更快,显然和对比 是一样的,显然后者增长的更快。

计算大O

首先在计算大O的时候我们需要约定一些东西用以简化计算,例如机器在执行代码的时候执行加法、减法、乘法、除法、赋值等等指令的时候,我们不要去关心它们谁快谁慢,应该把它们都看作一个时间单位,同时假设机器应该有足够的内存,不会发生问题等等。虽然现实生活中我们的假设很可能不存在,不过大O从来不是一个精确的值,能较好的估算就行。首先看一个简单的例子:

1    public static int sum(int N) {2        int partialSum = 0;3        for (int i=1; i<=N; i++) {4            partialSum += i*i*i;5        }6        return partialSum;7    }

我们先来详细分析一下这个程序,看它占执行需要花费多少时间。

  • 第二行变量声明不计时间,而初始化占1个时间单元。

  • 第6行返回值占用1个时间单元。

  • 第三行i的初始化占1个时间单元,自增运算增加N次占N个时间单元,i<=N逻辑运算,运算N+1次占用N+1个时间单元。

  • 第四行一个+和三个*都运算N次,总共占4N个时间单元。

那么程序总花销应该是1+1+1+N+N+1+4N=6N+4个时间单元。那么我们应该说这个程序是O(N)。N的系数以及后面跟的常数我们都给它简化了,因为它们不是一个量级。明白了吗?为什么要简化?想一想你每看到一个程序时都像刚才那样一行一行分析时间,你迟早会崩溃的,因此在计算大O的时候各种简化都有可能发生,比如我们还看这个示例程序,其实分析它的大O很简单,从上到下语句执行是O(1),而里面有个for循环,共循环N次,那么程序的大O就是O(N)了,不需要我们再复杂的去一条条计算。如上所讲一样,有时候分析程序时间复杂度往往看一部分就够了,在计算大O的时候可以使用这些一般法则:

  • for循环:一个for循环的运行时间应该至多是循环内的语句的运行时间乘以迭代的次数。

  • 嵌套for循环:应该从里向外分析这些循环,在一组嵌套循环内部的一条语句的总运行时间为该语句的运行时间乘以该组所有的for循环的大小的乘积。

  • 顺序语句:将各个语句的运行时间求和即可,最终大O就是最大的那个量级。

  • if/else语句:一个if/else语句的运行时间应该是判断花费的时间与各部分时间最长者之和。(注意如果这么算可能有时候会估计的过高,但是绝不会估计低。比如if中的运行时间是 ,而在实际判断中极少用到if中的语句块,那么就估计过高了,不过没关系,我们就是需要算最坏情况。)

知道了上边这写,那么对于简单的for循环、if判断语句等时间复杂度应该就会计算了,因为往往只需要看循环次数等就行了,不过可能对于log(N)之类的还不大会,那我们先看两个简单的例子:

public static long factorial( in N ) {    if (N <= 1)        return 1;    else        return N*factorial(N - 1);}

这是一个非常基础的递归程序,那么各位看它的时间复杂度应该为多少?很简单,总共递归了N-1次,并且没有额外花销,那么时间复杂度当然是O(N)。那么再看下面这个程序:

public static long fib( int N ) {    if (N <= 1)        return 1;    else        return fib(N-1) + fib(N-2);}

那这个程序时间复杂度是多少呢?乍一看好像每次递归都调用两次函数,那么是 ? 其实差不多,如果你去详细计算的话会发现它的时间复杂度应该为 之间,那么我们也可以看作是 。OK,上边两个应该没有什么问题,咱们看看诸如 之类的时间复杂度是怎么来的。还记得我们上次发过的最大子序列和的算法问题吗?其中有一个分治+递归的解法,它就是 的,如果忘了或者没看过的读者可以点击下方链接看一下。

最大子序列和-暴力法、分治+递归法、妙法

那个算法的程序如下:

public class maxVlaueSum2 {    public static void main(String[] args) {        // 初始化数组        int[] array = {-2,11,-4,13,-5,-2};        System.out.println(maxSum(array,0,array.length-1));    }

    /**     * 递归求解函数     * @param array :传入要解决的数组     * @param left  :要解决子问题的左界     * @param right :要解决子问题的右界     * @return  :返回三个部分的最大值     */    public static int maxSum(int[] array,int left,int right) {        //   BASE情况,如果递归到了最后一层,那么返回合适的值,小于0返回0,大于0返回该数。1            if(left == right) {2                if (array[left] > 0)3                    return array[left];4                else return 0;5            }6            int center = (left+right)/2;    //  取得数组中点7            int leftSubSUM = maxSum(array,left,center);     //  子问题左半部分最优解8            int rightSubSUM = maxSum(array,center+1,right);     //  子问题右半部分最优解

9            int leftNowSum = 0, maxleftSum = 0;     //  当前问题左半部分等待求和的当前值以及最大值

        //  遍历求包含左半部分最后一个值的最大值10            for (int i=center;i>=left;i--) {11                leftNowSum += array[i];12                if (leftNowSum > maxleftSum) {13                    maxleftSum = leftNowSum;14                }15            }16            int rightNowSum = 0,maxrightSum = 0;    //  当前问题右半部分等待求和的当前值以及最大值

        //  遍历求包含右半部分的第一个值的最大值17            for (int j=center+1;j<=right;j++) {18                rightNowSum += array[j];19                if (rightNowSum>maxrightSum) {20                    maxrightSum = rightNowSum;21                }22            }        //  返回当前问题的左半部分最大值、右半部分的最大值和横跨两部分最大值的最大值。即为当前问题的解。23            return Math.max(Math.max(leftSubSUM,rightSubSUM),(maxleftSum+maxrightSum));24        }25    }

我们看这个程序,先来简单分析一下。首先设 为求解大小为N的最大子序列和问题所花费的时间。假如数组只有一个元素,那么就是基准情况,这样的话程序从第4行就返回,时间复杂度为 ,这个时候 ,如果数组不止一个元素,那么程序会至少多执行两个递归调用两个for循环。对于两个for循环来讲,它们加起来的时间复杂度也才 ,也就是 ,除了两个递归调用外,其余的时间复杂的都为 ,而对于两个递归调用,首先我们需要明白它们做了什么,它们是把问题分成了两个相等大小的子问题,对于任一个递归调用来讲,它所花费的时间应该为 ,因此两个递归调用花费的时间之和应该为 ,那么程序的花费时间我们就算出来了,低量级的直接忽略,得到方程组:

为了简化计算,现用N代替上式中的 ,因为最终 还是要转化为大O来表示,因此这么做不会影响答案。由这个方程组可以解出来 的时间复杂度。来看第二个方程,首先等式两边同除以N,得到:

因为将式子中的N同时换为另一个式子时等式不变,那么式子可以做如下变化:

注意把N换成了 ,其中 从2到N,那么就有了上面那些式子。然后把所有式子等式两边分别求和,可以发现大部分都被消去,得到如下式子:

又因为 ,同时式子两边同乘以N,得到:

那么 的时间复杂度不就变成了 吗?明白了吗?

 • end • 

走在路上

goldsunC

最大k乘积的时间复杂度_惊,我还不会算时间复杂度!相关推荐

  1. a*算法的时间复杂度_数据结构(1)——算法和时间复杂度

    Data Structure 1 算法和时间复杂度 01.什么是数据结构? 程序设计 = 数据结构 + 算法 数据结构是关系,是数据元素相互之间存在的一种或多种特定关系的集合. 数据结构和算法凌驾于任 ...

  2. java快速排序的时间复杂度_程序猿必备排序算法及其时间复杂度分析

    常用的时间复杂度 常数阶\(O(1)\) 说明: 只要代码中没有复杂的循环条件,无论代码的函数是多少,一律为常数阶\(O(1)\) int i=1; int j=3; int m=0; m=i+j; ...

  3. 不会吧不会吧,不会真有人还不会算时间复杂度吧?用十分钟让你明白如何计算时间复杂度

    前言: 算法的分析方式有两种: 事后分析统计方法:编写算法对应程序,统计其执行时间. 存在问题:编写程序的语言不同,执行程序的环境不同等因素 事前估算分析方法:撇开上述因素,认为算法的执行时间是问题规 ...

  4. hashmap put过程_看完还不懂HashMap算我输(附互联网大厂面试常见问题)

    HashMap的原理与实现 版本之更迭: –>JDK 1.7 : Table数组+ Entry链表: –>JDK1.8 : Table数组+ Entry链表/红黑树:(为什么要使用红黑树? ...

  5. 一层循环时间复杂度_渐进时间复杂度分析

    公众号:原与译 直接看题: 给定一个自然数 n,然后求出前 n 个自然数的和 sum.( n > 0 ) 如: n = 3,则 sum = 1 + 2 + 3 = 6 n = 5,则 sum = ...

  6. 动态规划|最大k乘积问题(C语言)

    题目: [分析] 先通过若干个简单例子来观察规律,摸索思路.例如十进制整数 1234 划分为 3 段可有如下情形: 1 × 2 × 34 = 68 1 × 23 × 4 = 92 12 × 3 × 4 ...

  7. a*算法的时间复杂度_算法的时间和空间复杂度,就是这么简单

    算法(Algorithm) 算法是程序用来操作数据.解决程序问题的一组方法.对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别. 那么我们应该如何去 ...

  8. 冒泡和快速排序的时间复杂度_八大排序算法性能分析及总结

    一.排序算法说明 排序的定义:对一个无序的序列进行排序的过程. 输入:n个数:a1,a2,a3,-,an. 输出:n个数的排列:a1,a2,a3,-,an,使得a1<=a2<=a3< ...

  9. 一层循环时间复杂度_算法的时间与空间复杂度(一看就懂)

    算法(Algorithm)是指用来操作数据.解决程序问题的一组方法.对于同一个问题,使用不同的算法,也许最终得到的结果是一样的,但在过程中消耗的资源和时间却会有很大的区别. 那么我们应该如何去衡量不同 ...

最新文章

  1. html的子页面获取自己url,如何从html页面获取url参数并将其显示在textarea中?
  2. python常用内置函数总结-python常见的内置函数
  3. python os renames_Python3 os.renames() 方法
  4. 【Matlab】滤波器常用命令
  5. 实验吧-密码学-杯酒人生(特殊凯撒--维吉尼亚密码)(凯撒加解密脚本、维吉尼亚密码加解密脚本)...
  6. 【转载】可能是把Docker的概念讲的最清楚的一篇文章
  7. 递归获取拉平存储的树每个节点到达的路径
  8. 浏览器崩溃_安装谷歌浏览器后打开网页时出现页面崩溃的解决办法
  9. android双击返回键退出程序
  10. 凸优化有关的数值线性代数知识 3LU Cholesky和LDL因式分解
  11. UML之旅店预订系统
  12. gp数据库日常运维sql语句笔记
  13. Win10环境下初始化MySQL
  14. 闪存flash读写原理
  15. Netty傻瓜教程(四):bossGroup, workGroup?
  16. Windows笔记本-U盘无法完成格式化
  17. 基于json文件创建后端模拟接口
  18. 关于扩展IP地址空间的几个方案的探讨
  19. 幼师计算机课是上什么,幼师面试 鱼在天空飞,鸟在水里游是小班课程,还是中班,大班的课程...
  20. SpringBoot注解整理历史笔记

热门文章

  1. windows下安装使用WGET
  2. 一个很奇特的异常 tmpFile.renameTo(classFile) failed
  3. (转)一段如何調用Button.Click事件的故事
  4. 卡巴斯基许可Key需求登记表
  5. Python OS使用
  6. python如何实现接口安全_利用pypy沙箱模式实现安全的开放式Python用户编程接口的方法与流程...
  7. 功放(耳机/音箱)声压级计算(五)
  8. xcode与androidstudio 设置自定义主题
  9. Wpf之MVVM线程问题
  10. tensorflow之add_n