数据结构与算法学习笔记(二)

时间:2021-03-18 21:45:47
  1. 算法效率的度量方法:
    1. 事后统计法:通过设计好的测试程序和数据,利用计算机计时器对不同算法编制的程序的运行时间进行比较,从而确定算法效率的高低。缺陷:必须依据算法事先编制好测试程序,通常需要花费大量时间和精力。不同测试环境差别也很大。
    2. 事前分析估算法:在计算机程序编写签,依据统计方法对算法进行估算。
  2. 高级语言编写的程序在计算机上运行时所耗的时间取决于下列因素:
    1. 算法采用的策略,方案
    2. 编译产生的代码质量
    3. 问题的输入规模
    4. 机器执行指令的速度
  3. 研究算法的复杂度,侧重的是研究算法随着输入规模扩大增长量的一个抽象,而不是精确地定位需要执行多少次。在分析一个算法的运行时间时,重要的是把基本操作的数量和输入模式关联起来。
  4. 函数渐进增长:给定两个函数f(n)和g(n),如果存在一个整数N,使得对于所有的n>N,f(n)总是比g(n)大,那么,我们说f(n)的增长渐近快于g(n)。
  5. 判断一个算法的效率时,函数中的常数和其他次要项常常可以忽略,而更应该关注主项(最高项)的阶数。
  6. 算法的时间复杂度:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记做:T(n)=O(f(n))。它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称为时间复杂度。其中f(n)是问题规模n的某个函数。(这里的时间指的是执行的次数)
  7.  用大写O()来体现算法时间复杂度的记法,我们称之为大O记法。
  8. 一般情况下,随着输入规模n的增大,T(n)增长最慢的算法为最优算法。
  9. 如何分析一个算法的时间复杂度:
    1. 用常数1取代运行时间中的所有加法常数
    2. 在修改后的运行次数函数中,只保留最高阶项
    3. 如果最高阶项存在且不是1,则去除与这个项相乘的常数
    4. 得到最后的结果就是大O阶
    5. int sum = 0, n = 100;
      printf(
      "aaaaaaaaaaaa\n");
      printf(
      "aaaaaaaaaaaa\n");
      printf(
      "aaaaaaaaaaaa\n");
      printf(
      "aaaaaaaaaaaa\n");
      printf(
      "aaaaaaaaaaaa\n");
      printf(
      "aaaaaaaaaaaa\n");
      sum
      = (1+n)*n/2
      //这段代码的时间复杂度为O(1),因为这段代码执行了8次,没有与n相关,常数都简化为1
  10. 线性阶:一般含有非嵌套循环设计线性阶,线性阶就是随着问题规模n的扩大,对应计算次数呈直线增长。
    1. int i, n = 100, sum = 0;
      for(i = 0; i<n; i++) {
      sum
      = sum + i;
      }

       上面这段代码的循环复杂度为O(n),因为循环体中的代码需要执行n次

  11. 平方阶
    1. int i,j,n = 100;
      for(i = 0; i < n; i++) {
      for(j = 0; j < n; j++) {
      printf(
      "aaaaaaaaaaaa\n");
      }
      }

      上面这段代码的时间复杂度为O(n²)。

    2. int i,j,n = 100;
      for(i = 0; i < n; i++) {
      for(j = i; j < n; j++) {
      pringtf(
      "aaaaaaaaaaaaaa\n");
      }
      }

       分析,由于当i=0时,内循环执行了n次,当i=1时,内循环则执行n-1次。。。。。。当i=n-1时,内循环执行1次,所以总的执行次数应该是n+(n-1)+(n-2)+...+1=n(n+1)/2用上面的简化方法来简化½n²+½n,这个式子中没有常数项不用考虑第一条,根据第二条只保留最高项,去掉½n这一项,根据第三条去掉与最高项相乘的常数½,最终得到O(n²)

  12. 对数阶
    1. int i = 1, n = 100;
      while(i < n) {
      i
      = i*2;
      }

       由于每次i*2之后就距离n更近一步,假设有x个2相乘后大于或等于n,则会退出循环。于是由2x=n得到x=log2n,所以这个循环的时间复杂度为O(logn)。

    1. int i,j;
      for(i = 0; i < b; i++) {
      function(i);
      }

      void function(int count) {
      printf(
      "%d",count);
      }

       function函数的时间复杂度是O(1),所以整体的时间复杂度就是循环的次数O(n)。

    1. int i,j;
      for(i = 0; i < b; i++) {
      function(i);
      }

      void function(int count) {
      int j;
      for (j = count; j < n;j++) {
      printf(
      "%d",j);
      }
      }

       和平方阶中第二个例子一样,function内部的循环次数随count的增加而减少,所以根据方法简化后的时间复杂度为O(n²)

    1. n++;-------------------------------------执行1次
      function(n);-----------------------------执行n²次
      for(i = 0;i < n; i++) {------------------执行n²次
      function(i);
      }
      for(i = 0;i < n; i++) {------------------执行n²次
      for(j = i;j < n; j++) {
      printf(
      "%d",j);
      }
      }
      void function(int count) {
      int j;
      for (j = count; j < n;j++) {
      printf(
      "%d",j);
      }
      }

       上面几次操作是并列的,整体的执行次数应该是各个操作执行次数之和3n²+1,简化后得到时间复杂度为O(n²)

  13. 常见的时间复杂度
例子 时间复杂度 术语
5201314 O(1) 常数阶
3n+4 O(n) 线性阶
3n²+4n+5 O(n²) 平方阶
3log2n+4 O(logn) 对数阶
2n+3nlog2n+14 O(nlogn) nlogn阶
n3+2n2+4n+6 O(n3) 立方阶
2n O(2n) 指数阶

  17. 常见的时间复杂度所耗费的时间从小到大依次是

        O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < (nn)

  18. 平均运行时间是期望的运行时间

    19. 最坏运行时间是一种保证。在应用中,这是一种最重要的需求,通常除非特别指定,我们提到的运行时间都是最坏情况的运行时间。

  20. 算法的空间复杂度通过计算算法所需的存储空间实现,算法的空间复杂度的计算公式记做S(n) = O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。

  21. 通常,我们都是用“时间复杂度”来指运行时间的需求,用“空间复杂度”指空间需求。

  22. 当直接让我们求“复杂度”时,通常指的是时间复杂度。