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| \begin{matrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{matrix} \right| * \left| \begin{matrix} b_{11} & b_{12} \\ b_{21} & b_{22} \end{matrix} \right| = \left| \begin{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} \right| \]

例如:

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

方阵乘法的代码如下:

//方阵使用二位数组表示
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)$$。

posted @ 2020-08-05 03:58  Steve--DZC  阅读(322)  评论(0编辑  收藏  举报