自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

究竟為什么,快速排序的時間復(fù)雜度是n*lg(n)?

開發(fā) 開發(fā)工具
究竟為什么,快速排序,時間復(fù)雜度是O(n*lg(n))呢?今天就和大家聊聊時間復(fù)雜度。

最煩面試官問,“為什么XX算法的時間復(fù)雜度是OO”,今后,不再懼怕這類問題。

快速排序分為這么幾步:

第一步,先做一次partition;

partition使用第一個元素t=arr[low]為哨兵,把數(shù)組分成了兩個半?yún)^(qū):

  • 左半?yún)^(qū)比t大
  • 右半?yún)^(qū)比t小

第二步,左半?yún)^(qū)遞歸;

第三步,右半?yún)^(qū)遞歸;

偽代碼為:

  1. void quick_sort(int[]arr, int low, int high){ 
  2.          if(low== high) return; 
  3.          int i = partition(arr, low, high); 
  4.          quick_sort(arr, low, i-1); 
  5.          quick_sort(arr, i+1, high); 

為啥,快速排序,時間復(fù)雜度是O(n*lg(n))呢?

今天和大家聊聊時間復(fù)雜度。

畫外音:往下看,第三類方法很牛逼。

第一大類,簡單規(guī)則

為方便記憶,先總結(jié)幾條簡單規(guī)則,熱熱身。

規(guī)則一:“有限次操作”的時間復(fù)雜度往往是O(1)。

例子:交換兩個數(shù)a和b的值。

  1. void swap(int& a, int& b){ 
  2.          int t=a
  3.          a=b
  4.          b=t

分析:通過了一個中間變量t,進行了3次操作,交換了a和b的值,swap的時間復(fù)雜度是O(1)。

畫外音:這里的有限次操作,是指不隨數(shù)據(jù)量的增加,操作次數(shù)增加。

規(guī)則二:“for循環(huán)”的時間復(fù)雜度往往是O(n)。

例子:n個數(shù)中找到最大值。

  1. int max(int[] arr, int n){ 
  2.          int temp = -MAX; 
  3.          for(int i=0;i<n;++i) 
  4.                    if(arr[i]>temp) temp=arr[i]; 
  5.          return temp; 

分析:通過一個for循環(huán),將數(shù)據(jù)集遍歷,每次遍歷,都只執(zhí)行“有限次操作”,計算的總次數(shù),和輸入數(shù)據(jù)量n呈線性關(guān)系。

規(guī)則三:“樹的高度”的時間復(fù)雜度往往是O(lg(n))。

分析:樹的總節(jié)點個數(shù)是n,則樹的高度是lg(n)。

在一棵包含n個元素二分查找樹上進行二分查找,其時間復(fù)雜度是O(lg(n))。

對一個包含n個元素的堆頂元素彈出后,調(diào)整成一個新的堆,其時間復(fù)雜度也是O(lg(n))。

第二大類:組合規(guī)則

通過簡單規(guī)則的時間復(fù)雜度,來求解組合規(guī)則的時間復(fù)雜度。

例如:n個數(shù)冒泡排序。

  1. void bubble_sort(int[] arr, int n){ 
  2.    for(int i=0;i<n;i++) 
  3.        for(int j=0;j<n-i-1;j++) 
  4.            if(arr[j]>arr[j+1]) 
  5.                 swap(arr[j], arr[j+1]); 

分析:冒泡排序,可以看成三個規(guī)則的組合:

  • 外層for循環(huán)
  • 內(nèi)層for循環(huán)
  • 最內(nèi)層的swap

故,冒泡排序的時間復(fù)雜度為:

O(n) * O(n) * O(1) = O(n^2)

又例如:TopK問題,通過建立k元素的堆,來從n個數(shù)中求解最大的k個數(shù)。

先用前k個元素生成一個小頂堆,這個小頂堆用于存儲,當(dāng)前最大的k個元素。

接著,從第k+1個元素開始掃描,和堆頂(堆中最小的元素)比較,如果被掃描的元素大于堆頂,則替換堆頂?shù)脑?,并調(diào)整堆,以保證堆內(nèi)的k個元素,總是當(dāng)前最大的k個元素。

直到,掃描完所有n-k個元素,最終堆中的k個元素,就是為所求的TopK。

偽代碼:

  1. heap[k] = make_heap(arr[1, k]); 
  2. for(i=k+1 to n){ 
  3.          adjust_heap(heep[k],arr[i]); 
  4. return heap[k]; 

分析:可以看成三個規(guī)則的組合:

  • 新建堆
  • for循環(huán)
  • 調(diào)整堆

故,用堆求解TopK,時間復(fù)雜度為:

O(k) + O(n) * O(lg(k)) = O(n*lg(k))

畫外音:注意哪些地方用加,哪些地方用乘;哪些地方是n,哪些地方是k。

第三大類,遞歸求解

簡單規(guī)則和組合規(guī)則可以用來求解非遞歸的算法的時間復(fù)雜度。對于遞歸的算法,該怎么分析呢?

接下來,通過幾個案例,來說明如何通分析遞歸式,來分析遞歸算法的時間復(fù)雜度。

案例一:計算 1到n的和,時間復(fù)雜度分析。

如果用非遞歸的算法:

  1. int sum(int n){ 
  2.          int result=0
  3.          for(int i=0;i<n;i++) 
  4.                    result += i; 
  5.          return result; 

根據(jù)簡單規(guī)則,for循環(huán),sum的時間復(fù)雜度是O(n)。

但如果是遞歸算法,就沒有這么直觀了:

  1. int sum(int n){ 
  2.          if (n==1) return 1; 
  3.          return n+sum(n-1); 

如何來進行時間復(fù)雜度分析呢?

用f(n)來表示數(shù)據(jù)量為n時,算法的計算次數(shù),很容易知道:

(1) 當(dāng)n=1時,sum函數(shù)只計算1次

畫外音:if (n==1) return 1;

即:

f(1)=1【式子A】

(2) 不難發(fā)現(xiàn),當(dāng)n不等于1時:

f(n)的計算次數(shù),等于f(n-1)的計算次數(shù),再加1次計算

畫外音:return n+sum(n-1);

即:

f(n)=f(n-1)+1【式子B】

【式子B】不斷的展開,再配合【式子A】:

畫外音:這一句話,是分析這個算法的關(guān)鍵。

  1. f(n)=f(n-1)+1 
  2. f(n-1)=f(n-2)+1 
  3. … 
  4. f(2)=f(1)+1 
  5. f(1)=1 

上面共n個等式,左側(cè)和右側(cè)分別相加:

  1. f(n)+f(n-1)+…+f(2)+f(1) 
  2. [f(n-1)+1]+[f(n-2)+1]+…+[f(1)+1]+[1] 

即得到:

  1. f(n)=n 

已經(jīng)有那么點意思了哈,再來個復(fù)雜點的算法。

案例二:二分查找binary_search,時間復(fù)雜度分析。

  1. int BS(int[] arr, int low, int high, int target){ 
  2.          if (low>high) return -1; 
  3.          mid = (low+high)/2; 
  4.          if (arr[mid]== target) return mid; 
  5.          if (arr[mid]> target) 
  6.                   return BS(arr, low, mid-1, target); 
  7.          else 
  8.                   return BS(arr, mid+1, high, target); 

二分查找,單純從遞歸算法來分析,怎能知道其時間復(fù)雜度是O(lg(n))呢?

仍用f(n)來表示數(shù)據(jù)量為n時,算法的計算次數(shù),很容易知道:

(1) 當(dāng)n=1時,bs函數(shù)只計算1次

畫外音:不用糾結(jié)是1次還是1.5次,還是2.7次,是一個常數(shù)次。

即:

f(1)=1【式子A】

(2) 在n很大時,二分會進行一次比較,然后進行左側(cè)或者右側(cè)的遞歸,以減少一半的數(shù)據(jù)量:

f(n)的計算次數(shù),等于f(n/2)的計算次數(shù),再加1次計算

畫外音:計算arr[mid]>target,再減少一半數(shù)據(jù)量迭代

即:

f(n)=f(n/2)+1【式子B】

【式子B】不斷的展開,

  1. f(n)=f(n/2)+1 
  2. f(n/2)=f(n/4)+1 
  3. f(n/4)=f(n/8)+1 
  4. … 
  5. f(n/2^(m-1))=f(n/2^m)+1 

上面共m個等式,左側(cè)和右側(cè)分別相加:

  1. f(n)+f(n/2)+…+f(n/2^(m-1)) 
  2. [f(n/2)+1]+[f(n/4)+1]+…+[f(n/2^m)]+[1] 

即得到:

f(n)=f(n/2^m)+m

再配合【式子A】:

f(1)=1

即,n/2^m=1時, f(n/2^m)=1, 此時m=lg(n), 這一步,這是分析這個算法的關(guān)鍵。

將m=lg(n)帶入,得到:

f(n)=1+lg(n)

神奇不神奇?

最后,大boss,快速排序遞歸算法,時間復(fù)雜度的分析過程。

案例三:快速排序quick_sort,時間復(fù)雜度分析。

  1. void quick_sort(int[]arr, int low, inthigh){ 
  2.          if (low==high) return; 
  3.          int i = partition(arr, low, high); 
  4.          quick_sort(arr, low, i-1); 
  5.          quick_sort(arr, i+1, high); 

仍用f(n)來表示數(shù)據(jù)量為n時,算法的計算次數(shù),很容易知道:

當(dāng)n=1時,quick_sort函數(shù)只計算1次

f(1)=1【式子A】

在n很大時:

  • 第一步,先做一次partition;
  • 第二步,左半?yún)^(qū)遞歸;
  • 第三步,右半?yún)^(qū)遞歸;

即:

f(n)=n+f(n/2)+f(n/2)=n+2*f(n/2)【式子B】

畫外音:

(1)partition本質(zhì)是一個for,計算次數(shù)是n;

(2)二分查找只需要遞歸一個半?yún)^(qū),而快速排序左半?yún)^(qū)和右半?yún)^(qū)都要遞歸,這一點在分治法與減治法一章節(jié)已經(jīng)詳細(xì)講述過;

【式子B】不斷的展開, 

  1. f(n)=n+2*f(n/2) 
  2. f(n/2)=n/2+2*f(n/4) 
  3. f(n/4)=n/4+2*f(n/8) 
  4. … 
  5. f(n/2^(m-1))=n/2^(m-1)+2f(n/2^m) 

上面共m個等式,逐步帶入,于是得到:

  1. f(n)=n+2*f(n/2) 
  2. =n+2*[n/2+2*f(n/4)]=2n+4*f(n/4) 
  3. =2n+4*[n/4+2*f(n/8)]=3n+8f(n/8) 
  4. =… 
  5. =m*n+2^m*f(n/2^m) 

再配合【式子A】:

f(1)=1

即,n/2^m=1時, f(n/2^m)=1, 此時m=lg(n), 這一步,這是分析這個算法的關(guān)鍵。

將m=lg(n)帶入,得到:

  1. f(n)=lg(n)*n+2^(lg(n))*f(1)=n*lg(n)+n 

故,快速排序的時間復(fù)雜度是n*lg(n)。

wacalei,有點意思哈!

畫外音:額,估計83%的同學(xué)沒有仔細(xì)看,花5分鐘細(xì)思上述過程,一定有收獲。

總結(jié)

  • for循環(huán)的時間復(fù)雜度往往是O(n)
  • 樹的高度的時間復(fù)雜度往往是O(lg(n))
  • 二分查找的時間復(fù)雜度是O(lg(n)),快速排序的時間復(fù)雜度n*(lg(n))
  • 遞歸求解,未來再問時間復(fù)雜度,通殺

知其然,知其所以然。思路比結(jié)論重要。

【本文為51CTO專欄作者“58沈劍”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】

戳這里,看該作者更多好文 

 

責(zé)任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2020-09-08 15:40:58

算法快速排序堆排序

2021-10-15 09:43:12

希爾排序復(fù)雜度

2024-04-25 08:33:25

算法時間復(fù)雜度空間復(fù)雜度

2022-09-16 10:14:41

消息順序性分布式架構(gòu)

2018-07-31 09:52:38

機器學(xué)習(xí)排序算法圖像處理

2019-11-18 12:41:35

算法Python計算復(fù)雜性理論

2021-01-05 10:41:42

算法時間空間

2009-07-09 10:45:16

C#基本概念復(fù)雜度遞歸與接口

2022-02-22 10:11:01

系統(tǒng)軟件架構(gòu)

2024-05-20 09:04:29

時間復(fù)雜度代碼

2021-09-17 10:44:50

算法復(fù)雜度空間

2019-12-26 07:39:36

互聯(lián)網(wǎng)架構(gòu)ip

2017-11-03 11:02:08

數(shù)據(jù)庫中間件

2017-12-28 11:25:51

2014-12-10 09:23:14

2022-02-13 20:04:04

鏈表節(jié)點代碼

2020-12-30 09:20:27

代碼

2015-10-13 09:43:43

復(fù)雜度核心

2020-11-30 06:26:31

算法時間表示法

2021-07-20 11:38:55

算法計算機leetcode
點贊
收藏

51CTO技術(shù)棧公眾號