• 01.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)$$。

  • 相关阅读:
    为什么杜蕾斯的文案工资月薪5万?
    在独立音乐上,网易云音乐是如何甩了其他音乐平台几条街?
    两次大战,为什么德国成不了世界霸主呢?
    在大城市打拼的你,是想留下还是想攒够了钱回家?
    生存在互联网公司是种怎样的体验?
    5G为何采纳华为力挺的Polar码?一个通信工程师的大实话
    放下恩怨,曝小米中兴投关键性一票让华为顺利取得5G短码控制权
    中国唯一的科技城
    互联网圈的6大奇葩大产品经理:张小龙不在乎手机碎屏,马化腾让用户一秒变白痴
    为什么说中国快递分两种:一种叫顺丰,一种叫快递?
  • 原文地址:https://www.cnblogs.com/SteveDZC/p/13437444.html
Copyright © 2020-2023  润新知