zoukankan      html  css  js  c++  java
  • 时间复杂度

    时间复杂度

    1、时间频度

    时间复杂度通常是衡量算法的优劣的,衡量算法的时间严格来讲是很难衡量的,由于不同的机器性能不用环境都会造成不同的执行时间。算法的执行时间和语句的执行次数成正比,因此通过计算执行测试来推断执行时间。算法中语句执行次数称为语句频度或时间频度,记为T(n),n是问题的规模,T是Time,即时间频度。

    2、时间复杂度

    n不断变化时,T(n)也在不断变化,为了考察两者变化时呈现什么规律,可以使用时间复杂度来计算。

    通常操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),存在一个正常数c使得f(n) * c >= T(n)恒成立。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。

    简言之,只要计算一下语句的执行次数和n之间的关就可以了,去除系数部分,低阶项目部分,就是时间复杂度的值了。

    3、 时间复杂度的计算

    3.1 给定任意长度数组,读取数组第一个元素的值

    无论数组长度n为多少,代码执行一次。因此,时间复杂度为O(1)。

    //无论数组的长度n为多少,
    public void firstEle(int[] arr){
      return arr[0] ;					//代码只执行一次。
    }
    

    3.2 循环n次,输出hello world

    随着n的增大,打印函数逐渐增多。打印代码执行的次数是n,因此时间复杂度为O(n)。

    for(int i = 0 ; i < n ; i ++){
      //执行一次。
      System.out.println("hello world") ;
    }
    

    3.3 打印99乘法表

    打印99正方乘法表,打印语句输出9 * 9 = 81次。

    打印语句执行次数:$$n * n = n2$$,因此时间复杂度是$$O(n2)$$

    for(int i = 1 ; i <= n ; i ++){
      for(int j = 1 ; j <= n ; j ++){
        //打印语句的执行次数n * n次。
        System.out.print(j + " x " + i + "=" + (i * j));
      }
    }
    

    3.4 打印正三角的99乘法表

    正三角形状的99表格打印语句的执行次数为:

    [1 + 2 + 3 + ... + n = (n + 1) dfrac{n}{2} = dfrac{n^2}{2} + dfrac{n}{2} ]

    去掉系数部分和低阶部分,时间复杂度为仍然为$$O(n^2)$$

    for(int i = 1 ; i <= n ; i ++){
      for(int j = 1 ; j <= i ; j ++){
        //打印语句的执行次数n * n次。
        System.out.print(j + " x " + i + "=" + (i * j));
      }
    }
    

    3.5 冒泡排序

    冒泡排序是经典的排序算法是每次比较相邻的两个元素进行对调,进行多轮,直到数列有序。

    java_pro_001

    对于5个元素的冒泡排序来说,共需要比较4 + 3 + 2 + 1次比较。因此时间复杂度为:

    [n - 1 + n - 2 + ... 1 = dfrac{(n-1)}{2}n = dfrac{n^2}{2} - dfrac{n}{2} ]

    因此时间复杂度为:$$O(n^2)$$,这是冒泡排序最坏的情况下的时间复杂度。

    int[] arr = ... ;
    //外层比较n-1次
    for(int i = 0 ; i < arr.length - 1 ; i ++){
      //比较n-1-i次
      for(int j = 0 ; j < arr.length -1 - i ; j ++){
        int temp = arr[j] ;
        if(arr[j] > arr[j + 1]){
          arr[j] = arr[j + 1] ;
          arr[j + 1] = temp ;
        }
      }
    }
    

    冒泡排序可以进行优化,在最好的情况下,复杂度为O(n),思路如下:

    设置标志位flag=1,表示整个数列已经有序,就不需要再排序了,在里层循环中,如果发现没有进行过数据交换,就说明数列已经有序。

    在最好情况下,就是数列本身已经有序,只需要执行外层的n-1次循环即可,因此复杂度为O(n)。

    int[] arr = ... ;
    //外层比较n-1次
    for(int i = 0 ; i < arr.length - 1 ; i ++){
      boolean flag = true ;
      //比较n-1-i次
      for(int j = 0 ; j < arr.length -1 - i ; j ++){
        int temp = arr[j] ;
        if(arr[j] > arr[j + 1]){
          //发生交换,就设置标志位为false,即数列无序。
          flag = false ;
          arr[j] = arr[j + 1] ;
          arr[j + 1] = temp ;
        }
      }
      //判断如果是数列有序,则终止循环。
      if(flag){
        break ;
      }
    }
    

    3.6 折半查找

    折半查找也叫二分查找,是高效的查找方式,前提条件是数列需要时有序数列。图例如下:

    java_pro_002

    java_pro_003

    代码如下:

    //有序数组
    int[] arr = ... ;
    int min = arr[0] ;
    int max = arr[0] ;
    int mid = arr[arr.length/2] ;
    //目标元素
    int targ = 3 ;
    
    for(int min = 0 , max = arr.length - 1 ; min <= max  ; ){
      int mid = (min + max) / 2 ;
      //命中了
      if(arr[mid] == targ){
        return mid ;
      }
      //落在右侧范围
      else if(arr[mid] < targ){
        min = mid + 1 ;
      }
      //落在左侧范围
      else{
        max = mid - 1 ;
      } 
    }
    

    折半查找的最坏时间复杂度为:以2为底,n的对数。

    [O(log_2 n ) ]

    3.7 hashmap

    哈希map是java中最重要的集合之一,设计非常巧妙,使用通过数组+链表方式组合实现,哈希的目的是让对象在空间内尽可能分散。那么HashMap的时间复杂度是多少呢?

    如果hashmap的每个桶内的元素个数最多不会超过一个常数C,当增加元素时,不断增加桶的数量,而保证每个桶内的元素量固定,因此就可以保证最快的查询速度,则时间复杂度就是$$O(C*n^2)$$,去掉系数部分就是

    [ O(n^0) = O(1) ]

    如果在最坏的情况下就是所有元素进入同一个桶,导致给桶内的链条非常长,则检索数据时,时间复杂度为:

    [ O(n) ]

    3.8 矩阵的乘积

    矩阵我们按照阶数为n的两个方阵进行计算,矩阵的乘法运算法则为:

    [left| egin{matrix} a_{11} & a_{12} \ a_{21} & a_{22} end{matrix} ight| * left| egin{matrix} b_{11} & b_{12} \ b_{21} & b_{22} end{matrix} ight| = left| egin{matrix} a_{11} * b_{11}+a_{12} *b_{21} &a_{11} * b_{12}+a_{12} *b_{22} \ a_{21} * b_{11}+a_{22} *b_{21} &a_{21} * b_{12}+a_{22} *b_{22} end{matrix} ight| ]

    例如:

    [left| egin{matrix} 0 & 1 \ 1 & 1 \ end{matrix} ight| * left| egin{matrix} 1 & 2 \ 3 & 4 \ end{matrix} ight| = left| egin{matrix} 0*1+1*3 & 0*2+1*4 \ 1*1+1*3 & 1*2+1*4 \ end{matrix} ight| = left| egin{matrix} 3 &4 \ 4 & 6 \ end{matrix} ight| ]

    方阵乘法的代码如下:

    //方阵使用二位数组表示
    int[][] a = ... ;
    int[][] b = ... ;
    //结果矩阵
    int[][] r = ... ;
    //[思路]
    //从结果矩阵出发,结果矩阵也是一个阶数为n的方阵,但是每个对应的元素
    //循环n次计算累加和得到。     
    //循环行数
    for(int i = 0 ; i < a.length ; i ++){
      //循环列的个数,这里仍然是m.length是因为方阵
      for(int j = 0 ; j < b.length ; j ++){
        for(int k = 0 ; k < a.length ; k ++){
          r[i][j] = r[i][j] + a[i][k] * b[k][j] ; 
        }
      }
    }
    

    方阵的矩阵乘法的计算次数为$$n3$$,因此时间复杂度为$$O(n3)$$。

  • 相关阅读:
    hdu5360 Hiking(水题)
    hdu5348 MZL's endless loop(欧拉回路)
    hdu5351 MZL's Border(规律题,java)
    hdu5347 MZL's chemistry(打表)
    hdu5344 MZL's xor(水题)
    hdu5338 ZZX and Permutations(贪心、线段树)
    hdu 5325 Crazy Bobo (树形dp)
    hdu5323 Solve this interesting problem(爆搜)
    hdu5322 Hope(dp)
    Lightoj1009 Back to Underworld(带权并查集)
  • 原文地址:https://www.cnblogs.com/xupccc/p/9544583.html
Copyright © 2011-2022 走看看