计算时间复杂度

1.计算时间复杂度

c09c7f12-ba75-4f45-9423-0194e6462a51

1.1 时间复杂度概念

如何衡量一个算法的好与坏 ?

  public static long Fib(int N){
          if(N < 3){
              return 1;
          }
  ​
          return Fib(N-1) + Fib(N-2);
  }

比如,如何判断 上面这个递归解斐波那契数列 算法的好坏 ?

判断一个算法好坏可以从两个方面:

  1. 时间效率

  2. 空间效率

时间效率和空间效率 也被称为时间复杂度空间复杂度

什么是时间复杂度 ?

在定义中,时间复杂度是一个数学函数,这个函数描述一个算法的运行时间

如何计算一个算法的运行时间 ?

掐表数 这个算法编写成的程序 在机器上的运行时间 ?这个方法不可行

  1. 很麻烦 每计算一个算法的时间复杂度 都需要上机测试

  2. 由于机器配置的不同 程序执行的时间也就不同

所以 我们需要抛去这些因素,用一个统一的标准去计算一个算法执行的时间

一个算法执行的时间与执行程序语句的数次有关联 一个程序执行语句的次数越多 需要的时间也就越多

且计算语句执行的次数不需要上机测试 也与机器的配置无关

所以,计算一个算法的运行时间 只需要去计算程序语句执行的次数

练习:

// 计算func1函数执行了多少次语句?
  public static void func1(int N) {
          int count = 0;
          for (int i = 0; i < N; i++) {
              for (int j = 0; j < N; j++) {
                  count++;
              }
          }
          for (int k = 0; k < 2 * N ; k++) {
              count++;
          }
  ​
          int M = 10;
          while ((M--) > 0) {
              count++;
          }
  ​
          System.out.println(count);
      }

F(N) = n^{_{_{}}}*n + 2n + 10

1.2 大O渐进表示法

计算一个算法运行时间时 是否需要计算出程序语句的具体的执行次数 ?

结论是不需要 以下例证明:

// 计算func1函数执行了多少次语句?
  public static void func1(int N) {
          int count = 0;
          for (int i = 0; i < N; i++) {
              for (int j = 0; j < N; j++) {
                  count++;
              }
          }
          for (int k = 0; k < 2 * N ; k++) {
              count++;
          }
  ​
          int M = 10;
          while ((M--) > 0) {
              count++;
          }
  ​
          System.out.println(count);
      }

语句具体执行次数:F(N) = n * n + 2 * n + 10

N = 10 ——》F(N) = 100 + 20 + 10 N = 100 ——》F(N) = 10000 + 200 + 10 N = 1000 ——》F(N) = 1000000 + 2000 + 10

通过观察发现,当N越大 等式后两项(2n+10) 对结果的影响越小

当N无限大时,等式后两项可以完全忽略不计 因为去除这两项之和 对最后结果没有影响

所以,我们在计算执行语句次数时 只需要去计算大概的语句执行次数

什么是大O渐进表示法 ? 它用来做什么 ?

大O渐进表示法是一个描述函数渐进行为的数学符号,它用来描述时间和空间复杂度

什么是描述函数渐进行为 ?

F(N) = N * N

数据个数N = 10 ——》执行语句次数 = 100

数据个数N = 100 ——》执行语句次数 = 10000

数据个数N = 1000 ——》执行语句次数 = 1000000

F(N) = N * N ——大O渐进表示——》 O(N * N)表示数据个数为N,执行语句次数为N*N

例子:

// 1. 计算func2的时间复杂度?
  void func2(int N) {
      int count = 0;
   
      for (int k = 0; k < 2 * N ; k++) {
          count++;
      }
   
      int M = 10;
      while ((M--) > 0) {
          count++;
      }
   
      System.out.println(count);
  }

F(N) = 2 * N + 10 ——》func2的时间复杂度为 O(N)

当N无限大时,系数2和+10都可以忽略不计

// 2. 计算func4的时间复杂度?
  void func4(int N) {
      int count = 0;
   
      for (int k = 0; k < 100; k++) {
          count++;
      }
   
      System.out.println(count);
  }

执行常数次 ——》func2的时间复杂度为 O(1)

注意:在大O的渐进表示法中,O(1)表示执行常数次语句

// 3. 计算bubbleSort的时间复杂度?
  void bubbleSort(int[] array) {
      for (int end = array.length; end > 0; end--) {
          boolean sorted = true;
          for (int i = 1; i < end; i++) {
              if (array[i - 1] > array[i]) {
                  Swap(array, i - 1, i);
                  sorted = false;
              }
          }
   
          if (sorted == true) {
              break;
          }
      }
  }

最好情况: array = {1, 2, 3, 4, 5};

内循环遍历一次 ——》时间复杂度为 O(N)

最坏情况: array = {5, 4, 3, 2, 1};

4,3,2,1,5 —— 执行(n-1)次

3,2,1,4,5 —— 执行(n-2)次

.....

2,1,3,4,5 —— 执行2次

1,2,3,4,5 —— 执行1次

观察发现这是个等差数列,根据等差数列求和公式 ((首项 + 尾项) * 项数)/2

((n-1) + 1) * (n-1) / 2 = 1/2n^2 - 1/2n ——》时间复杂度为O(N^2)

一般情况下 最后的结果都取最坏的情况

最后一定需要注意 判断嵌套循环的时间复杂度需要根据代码逻辑进行判断

在这个冒泡排序例子中 核心逻辑在内循环 计算内循环的执行次数就可以得到最终的结果

如果数据量很大 冒泡排序绝大部分的执行次数都是在内循环上 外循环的执行次数对结果不会有影响

// 4. 计算binarySearch的时间复杂度?
      public static int binarySearch(int[] array, int value) {
          int begin = 0;
          int end = array.length - 1;
          while (begin <= end) {
              int mid = begin + ((end-begin) / 2);
              if (array[mid] < value)
                  begin = mid + 1;
              else if (array[mid] > value)
                  end = mid - 1;
              else
                  return mid;
          }
  ​
          return -1;
      }

最坏情况: 最后一次才找到,n个数据查找了x次:

// 5. 计算阶乘递归factorial的时间复杂度?
  long factorial(int N) {
      return N < 2 ? N : factorial(N-1) * N;
  }

在大部分情况下 递归执行次数 = 递归次数 * 每个递归中语句执行次数

  // 计算斐波那契递归fibonacci的时间复杂度?
  int fibonacci(int N) {
      return N < 2 ? N : fibonacci(N-1)+fibonacci(N-2);
  }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值