圖解時間複雜度
寫在前面:
這篇文章是在公眾號: 程式設計師小灰 中釋出的。是我到目前為止所看到的關於時間複雜度介紹的最好的文章,簡介 清晰 明瞭。
所以拿來po出來 僅供學習交流,如侵則刪。
現已將此文收錄至: 《資料結構》| 第一章 緒論 知識梳理
正文:
時間複雜度的意義
究竟什麼是時間複雜度呢?讓我們來想象一個場景:某一天,小灰和大黃同時加入了一個公司......
一天過後,小灰和大黃各自交付了程式碼,兩端程式碼實現的功能都差不多。大黃的程式碼執行一次要花100毫秒,記憶體佔用5MB。小灰的程式碼執行一次要花100秒,記憶體佔用500MB。於是......
由此可見,衡量程式碼的好壞,包括兩個非常重要的指標:
1.執行時間;
2.佔用空間。
基本操作執行次數
關於程式碼的基本操作執行次數,我們用四個生活中的場景,來做一下比喻:
場景1:給小灰一條長10寸的麵包,小灰每3天吃掉1寸,那麼吃掉整個麵包需要幾天?
答案自然是 3 X 10 = 30天。
如果麵包的長度是 N 寸呢?
此時吃掉整個麵包,需要 3 X n = 3n 天。
如果用一個函式來表達這個相對時間,可以記作 T(n) = 3n。
場景2:給小灰一條長16寸的麵包,小灰每5天吃掉麵包剩餘長度的一半,第一次吃掉8寸,第二次吃掉4寸,第三次吃掉2寸......那麼小灰把麵包吃得只剩下1寸,需要多少天呢?
這個問題翻譯一下,就是數字16不斷地除以2,除幾次以後的結果等於1?這裡要涉及到數學當中的對數,以2位底,16的對數,可以簡寫為log16。
因此,把麵包吃得只剩下1寸,需要 5 X log16 = 5 X 4 = 20 天。
如果麵包的長度是 N 寸呢?
需要 5 X logn = 5logn天,記作 T(n) = 5logn。
場景3:給小灰一條長10寸的麵包和一個雞腿,小灰每2天吃掉一個雞腿。那麼小灰吃掉整個雞腿需要多少天呢?
答案自然是2天。因為只說是吃掉雞腿,和10寸的麵包沒有關係 。
如果麵包的長度是 N 寸呢?
無論麵包有多長,吃掉雞腿的時間仍然是2天,記作 T(n) = 2。
場景4:給小灰一條長10寸的麵包,小灰吃掉第一個一寸需要1天時間,吃掉第二個一寸需要2天時間,吃掉第三個一寸需要3天時間.....每多吃一寸,所花的時間也多一天。那麼小灰吃掉整個麵包需要多少天呢?
答案是從1累加到10的總和,也就是55天。
如果麵包的長度是 N 寸呢?
此時吃掉整個麵包,需要 1+2+3+......+ n-1 + n = (1+n)*n/2 = 0.5n^2 + 0.5n。
記作 T(n) = 0.5n^2 + 0.5n。
上面所講的是吃東西所花費的相對時間,這一思想同樣適用於對程式基本操作執行次數的統計。剛才的四個場景,分別對應了程式中最常見的四種執行方式:
場景1:T(n) = 3n,執行次數是線性的。
-
void eat1(int n){
-
for(int i=0; i<n; i++){;
-
System.out.println("等待一天");
-
System.out.println("等待一天");
-
System.out.println("吃一寸麵包");
-
}
-
}
-
vo
場景2:T(n) = 5logn,執行次數是對數的。
-
void eat2(int n){
-
for(int i=1; i<n; i*=2){
-
System.out.println("等待一天");
-
System.out.println("等待一天");
-
System.out.println("等待一天");
-
System.out.println("等待一天");
-
System.out.println("吃一半面包");
-
}
-
}
場景3:T(n) = 2,執行次數是常量的。
-
void eat3(int n){
-
System.out.println("等待一天");
-
System.out.println("吃一個雞腿");
-
}
場景4:T(n) = 0.5n^2 + 0.5n,執行次數是一個多項式。
-
void eat4(int n){
-
for(int i=0; i<n; i++){
-
for(int j=0; j<i; j++){
-
System.out.println("等待一天");
-
}
-
System.out.println("吃一寸麵包");
-
}
-
}
漸進時間複雜度
有了基本操作執行次數的函式 T(n),是否就可以分析和比較一段程式碼的執行時間了呢?還是有一定的困難。
比如演算法A的相對時間是T(n)= 100n,演算法B的相對時間是T(n)= 5n^2,這兩個到底誰的執行時間更長一些?這就要看n的取值了。
所以,這時候有了漸進時間複雜度(asymptotic time complectiy)的概念,官方的定義如下:
若存在函式 f(n),使得當n趨近於無窮大時,T(n)/ f(n)的極限值為不等於零的常數,則稱 f(n)是T(n)的同數量級函式。
記作 T(n)= O(f(n)),稱O(f(n)) 為演算法的漸進時間複雜度,簡稱時間複雜度。
漸進時間複雜度用大寫O來表示,所以也被稱為大O表示法。
如何推匯出時間複雜度呢?有如下幾個原則:
-
如果執行時間是常數量級,用常數1表示;
-
只保留時間函式中的最高階項;
-
如果最高階項存在,則省去最高階項前面的係數。
讓我們回頭看看剛才的四個場景。
場景1:
T(n) = 3n
最高階項為3n,省去係數3,轉化的時間複雜度為:
T(n) = O(n)
場景2:
T(n) = 5logn
最高階項為5logn,省去係數5,轉化的時間複雜度為:
T(n) = O(logn)
場景3:
T(n) = 2
只有常數量級,轉化的時間複雜度為:
T(n) = O(1)
場景4:
T(n) = 0.5n^2 + 0.5n
最高階項為0.5n^2,省去係數0.5,轉化的時間複雜度為:
T(n) = O(n^2)
這四種時間複雜度究竟誰用時更長,誰節省時間呢?稍微思考一下就可以得出結論:
O(1)< O(logn)< O(n)< O(n^2)
在程式設計的世界中有著各種各樣的演算法,除了上述的四個場景,還有許多不同形式的時間複雜度,比如:
O(nlogn), O(n^3), O(m*n),O(2^n),O(n!)
今後遨遊在程式碼的海洋裡,我們會陸續遇到上述時間複雜度的演算法。
時間複雜度的巨大差異
我們來舉過一個栗子:
演算法A的相對時間規模是T(n)= 100n,時間複雜度是O(n)
演算法B的相對時間規模是T(n)= 5n^2,時間複雜度是O(n^2)
演算法A執行在小灰家裡的老舊電腦上,演算法B執行在某臺超級計算機上,執行速度是老舊電腦的100倍。
那麼,隨著輸入規模 n 的增長,兩種演算法誰執行更快呢?
從表格中可以看出,當n的值很小的時候,演算法A的執行用時要遠大於演算法B;當n的值達到1000左右,演算法A和演算法B的執行時間已經接近;當n的值越來越大,達到十萬、百萬時,演算法A的優勢開始顯現,演算法B則越來越慢,差距越來越明顯。
這就是不同時間複雜度帶來的差距。
相關文章
- 時間複雜度(詳解)時間複雜度
- 時間複雜度跟空間複雜度時間複雜度
- 時間複雜度與空間複雜度時間複雜度
- 時間複雜度和空間複雜度時間複雜度
- 時間複雜度O(n)和空間複雜度時間複雜度
- 122 演算法的時間複雜度和空間複雜度詳解演算法時間複雜度
- 時間複雜度怎麼算?如何計算時間複雜度?時間複雜度
- 那些年忽略的知識:時間複雜度和空間複雜度詳解時間複雜度
- 淺談時間複雜度時間複雜度
- dfs時間複雜度分析時間複雜度
- 時間與空間複雜度分析複雜度
- 時間複雜度的計算時間複雜度
- 解惑3:時間頻度,演算法時間複雜度演算法時間複雜度
- 最詳細的解說—時間和空間複雜度複雜度
- PHP 演算法基礎----時間複雜度和空間複雜度(轉載)PHP演算法時間複雜度
- 演算法的時間複雜度演算法時間複雜度
- 演算法(一)時間複雜度演算法時間複雜度
- 資料結構:時間複雜度資料結構時間複雜度
- 氣泡排序時間複雜度分析排序時間複雜度
- 常用的時間複雜度分析方法時間複雜度
- 演算法分析__時間複雜度演算法時間複雜度
- 卷演算法——時間複雜度演算法時間複雜度
- 易被忽略的知識點之 ---- 各種時間複雜度和空間複雜度時間複雜度
- 簡單程式的時間複雜度分析時間複雜度
- 冰與火之歌:「時間」與「空間」複雜度複雜度
- 說說你對演算法中時間複雜度,空間複雜度的理解?如何計算?演算法時間複雜度
- 遞迴演算法的時間複雜度遞迴演算法時間複雜度
- 時間複雜度 – Java那些事兒專欄時間複雜度Java
- 特別容易理解的時間複雜度文章時間複雜度
- 平均和最壞時間複雜度介紹時間複雜度
- 資料結構與演算法(一):帶你瞭解時間複雜度和空間複雜度到底是什麼?資料結構演算法時間複雜度
- 如何不用演算法對陣列進行(0時間複雜度,0空間複雜度)排序?演算法陣列時間複雜度排序
- 自學 資料結構四月二十一日_時間複雜度&空間複雜度資料結構時間複雜度
- 資料結構-邏輯關係&物理關係、時間複雜度、空間複雜度、順序表資料結構時間複雜度
- 一文講透演算法中的時間複雜度和空間複雜度計算方式演算法時間複雜度
- 我們常說的演算法時間複雜度和空間複雜度到底是什麼?演算法時間複雜度
- 1. 時間複雜度和空間複雜度 (7 天掌握演算法面試必考知識點)時間複雜度演算法面試
- 看動畫輕鬆理解時間複雜度(一)動畫時間複雜度