概述
尷尬,學妹問我“氣泡排序、二分查詢、希爾排序、快速排序方法”演算法的『時間複雜度』,我只能使用百度查詢答案進行了回答,但這不符合我的人設,我必須要弄懂這個東西。
作為一個「不稱職的攻城獅」,對複雜度的概念是很模糊的,更不要說去計算複雜度了。
但是在開發中對於程式碼快的執行,做到“提高程式碼執行率、降低記憶體佔用率”,巧了,這和我百度瞭解到的『複雜度』相似,然後查詢了相關資料,進行一個複習歸納。
簡單來說,就是同一個功能:
- 別人寫出來執行,記憶體佔用100M,執行時間需要1秒;
- 自己寫出來執行,記憶體佔用500M,執行時間需要5秒。
這你能忍?但是在程式碼開發中是沒辦法計算記憶體佔用和執行時間情況的,怎麼樣才能預估程式碼塊達到了較優呢?而且在不同的裝置上執行的效果也不同,
比如你新買的手機和用了5年的手機,開啟同一個軟體所需要的時間肯定是不同的。
但是我們基本的程式碼塊執行次數是可以計算的,這就是我們今天的主角『複雜度』。
PS:衡量不同演算法之間的優劣就要用到時間複雜度和空間複雜度。
時間複雜度
什麼是時間複雜度?舉個例子:
public string attack(int n) { for (int i = 0; i < n; i++) { Console.WriteLine("被攻擊"); } return "快來救我"; }
上面程式碼執行如下:
i = 0 : 執行 1 次 i < n : 執行 n+1 次 i++ : 執行 n+1 次 Console.WriteLine("被攻擊"); : 執行 n 次 return "快來救我" : 執行 1 次
這個方法的總執行次數就是 3n + 4 次,
但是開發過程中不可能都這樣去數,所以根據程式碼執行時間推導過程就有一個規律,這就是所有程式碼執行時間 T(n)和程式碼的執行次數 f(n) ,這個是成正比的,而這個規律有一個公式(大歐表示法):
T(n) = O(f(n))
n 是輸入資料的大小或者輸入資料的數量
T(n) 表示一段程式碼的總執行時間
f(n) 表示一段程式碼的總執行次數
O 表示程式碼的執行時間 T(n) 和 執行次數f(n) 成正比
套用公式可以得到上面方法的複雜度就是:O(3n+4),簡化為O(n)。
為什麼可以簡化為O(n)呢,我們看下簡化過程。
- 如果只是常數直接估算為1,O(3) 的時間複雜度就是 O
(1)
,不是說只執行了1次,而是對常量級時間複雜度的一種表示法。一般情況下,只要演算法裡沒有迴圈和遞迴,就算有上萬行程式碼,時間複雜度也是O(1) - O(3n+4) 裡常數4對於總執行次數的幾乎沒有影響,直接忽略不計,係數 3 影響也不大,因為3n和n都是一個量級的,所以作為係數的常數3也估算為1或者可以理解為去掉係數,所以 O(3n+4) 的時間複雜度為 O
(n)
- 如果是多項式,只需要保留n的最高次項,O
( 666n³ + 666n² + n )
,這個複雜度裡面的最高次項是n的3次方。因為隨著n的增大,後面的項的增長遠遠不及n的最高次項大,所以低於這個次項的直接忽略不計,常數也忽略不計,簡化後的時間複雜度為 O(n³)
是不是很簡單,我們看下常用的時間複雜度。
1、常數階 O(1)
通過上面的簡化過程知道,一般情況下,只要演算法裡沒有迴圈和遞迴,就算有上萬行程式碼,時間複雜度也是 O(1)
,因為它的執行次數不會隨著任何一個變數的增大而變長,比如下面這樣
public string attack(int n) { int power = 100; int speed = 65; Console.WriteLine("力量是:", power); Console.WriteLine("速度是:", speed); if (power == 200) Console.WriteLine("快來救我"); ..... return "我沒事,不用擔心"; }
2、線性階 O(n)
只有一層迴圈或者遞迴等,時間複雜度就是 O(n)
,這樣消耗時間隨著N變化而變化,比如下面這種
public string attack(int n) { for (int i = 0; i < n; i++) { Console.WriteLine("我沒事"); } return "我很好"; } public string runaway(int n) { while(--n>0) Console.WriteLine("跑跑跑"); return "我跑的很快"; } public string gameover(int n) { Console.WriteLine("hhh"); if (--n > 0) { Console.WriteLine(gameover(n)); } return "結束"; }
3、平方階 O(n²)
就是雙重for迴圈,如果外層的是n,內層的是m,那麼複雜度就是O(m*n),比如下面這種
public string attack(int n) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { Console.WriteLine("我沒事"); } } return "我很好"; }
還記得上面的簡化不?這樣的,總執行次數為 n + n²,如果是多項式,取最高次項,所以這個時間複雜度也是 O(n²)
public string attack(int n) { for (int i = 0; i < n; i++) { Console.WriteLine("我沒事"); } for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { Console.WriteLine("我沒事"); } } return "我很好"; }
4、對數階 O(logN)
下面的迴圈就按i*2何時=N,就是2的多少次方是N,即log2^N,讀作以2為底N的對數。對數公式[1] log不瞭解的可以點選檢視一下,這裡不做過多講解。
int i = 1; while (i < n) i = i * 2;
5、線性對數階 O(nlogN)
就是for迴圈套while,如下nlog2^N,讀作n倍以2為底N的對數。。對數公式[1] log不瞭解的可以點選檢視一下,這裡不做過多講解。
for (int j = 0; j < n; j++) { int i = 1; while (i < n) i = i * 2; }
6、更多...
其他還有一些時間複雜度的運用,下面由快到慢排列了一下:
複雜度 | 名稱 |
---|---|
O(1) |
常數複雜度 |
O(logn) |
對數複雜度 |
O(n) |
線性時間複雜度 |
O(nlogn) |
線性對數複雜度 |
O(n²) |
平方 |
O(n³) |
立方 |
O(2^ n ) |
指數,一點資料量就卡的不行 |
O(n!) |
階乘,就更慢了 |
這些時間複雜度有什麼區別呢,看張圖
隨著資料量或者 n 的增大,時間複雜度也隨之增加,也就是執行時間的增加,會越來越慢,越來越卡。
總的來說時間複雜度就是執行時間增長的趨勢,那麼空間複雜度就是儲存空間增長的趨勢。
空間複雜度
空間複雜度就是演算法需要多少記憶體,佔用了多少空間。
常用的空間複雜度有 O(1)
、O(n)
、O(n²)。
O(1) 就是演算法執行臨時空間不會隨著N發生變化 都算成一個常量,如下。
public string attack(int n) { int j = 0; for (int i = 0; i < n; i++) { j = i; j++; } return "我很好"; }
O(n)就是一開始就開闢的記憶體,不會在改變,如下new出來了N個,但是N下面迴圈並沒有再開臨時變數
public string attack(int n) { int[] m = new int[n]; int j = 0; for (int i = 1; i < n; i++) { j = i; j++; } return "我很好"; }
『空間複雜度』和『時間複雜度』判斷標準差不多,主要是看開了多少個臨時變數,是否跟N有一定的線性關係。
這都是一些簡單的,如果是複雜的怎麼計算呢, 下面都計算時間複雜度為例子:
- T(n) = n + 29 一般說是O(n)因為常數項影響函式增長很小
- T(n) = n^3 + n^2 + 29 一般說為O(n3),n3 的增長速度是遠超 n^2 的,同時 n^2 的增長速度是遠超 n 的,所以有了高次項可以忽略低次項
- T(n) = 3n^3 一般說為O(n^3)因為階數比乘數影響增長速度最顯著我們通常忽略
總結
重新梳理了一遍以後,現在寫程式碼眼裡浮現的都是一堆O(1)、O(n)、O(n²)、logN,感覺要瘋了。
參考資料
2.如何理解時間複雜度和空間複雜度:https://www.cnblogs.com/magicg/p/15178750.html
3.C#演算法的時間複雜度和空間複雜度:https://blog.csdn.net/q_17600689511/article/details/100189543
4.c#演算法時間複雜度 面試必看:https://blog.csdn.net/weixin_44370124/article/details/119321322
歡迎關注訂閱微信公眾號【熊澤有話說】,更多好玩易學知識等你來取
作者:熊澤-學習中的苦與樂 公眾號:熊澤有話說 出處: https://www.cnblogs.com/xiongze520/p/15666448.html 您可以隨意轉載、摘錄,但請在文章內註明作者和原文連結。
|