時間複雜度和空間複雜度學習
1.名詞定義解釋
時間複雜度:就是執行演算法需要消耗的時間,越快越好。
空間複雜度:就是執行演算法需要消耗的空間大小,越小越好。
2.時間複雜度的計算
一般用大寫的O表示T(n) = O(f(n)); n是影響複雜度變化的因子,f(n)是複雜度具體的演算法。
常見的時間複雜度量級
- 常數階O(1)
- 線性階O(n)
- 對數階O(logN)
- 線性對數階O(nlogN)
- 平方階O(n²)
- 立方階O(n³)
- K次方階O(n^k)
- 指數階(2^n)
接下來再看一下不同的複雜度所對應的演算法型別。
常數階O(1)
int a = 1; int b = 2; int c = 3;
我們假定每執行一行程式碼所需要消耗的時間為1個時間單位,那麼以上3行程式碼就消耗了3個時間單位。那是不是這段程式碼的時間複雜度表示為O(n)呢 ?
其實不是的,因為大O符號表示法並不是用於來真實代表演算法的執行時間的,它是用來表示程式碼執行時間的增長變化趨勢的。
上面的演算法並沒有隨著某個變數的增長而增長,那麼無論這類程式碼有多長,即使有幾萬幾十萬行,都可以用O(1)來表示它的時間複雜度。
線性階O(n)
for(i = 1; i <= n; i++) { j = i; j++; }
看這段程式碼會執行多少次呢?
第1行會執行1次,第2行和第3行會分別執行n次,總的執行時間也就是 2n + 1 次,那它的時間複雜度表示是 O(2n + 1) 嗎? No !
所以它的時間複雜度其實是O(n);
對數階O(logN)
int i = 1; while(i < n) { i = i * 2; }
可以看到每次迴圈的時候 i 都會乘2,那麼總共迴圈的次數就是log2n,因此這個程式碼的時間複雜度為O(logn)。
這兒有個問題,為什麼明明應該是O(log2n),卻要寫成O(logn)呢?
其實這裡的底數對於研究程式執行效率不重要,寫程式碼時要考慮的是資料規模n對程式執行效率的影響,常數部分則忽略,同樣的,如果不同時間複雜度的倍數關係為常數,那也可以近似認為兩者為同一量級的時間複雜度。
線性對數階O(nlogN)
for(m = 1; m < n; m++) { i = 1; while(i < n) { i = i * 2; } }
線性對數階O(nlogN) 其實非常容易理解,將時間複雜度為O(logn)的程式碼迴圈N遍的話,那麼它的時間複雜度就是 n * O(logN),也就是了O(nlogN)。
平方階O(n²)
for(x = 1; i <= n; x++){ for(i = 1; i <= n; i++) { j = i; j++; } }
把 O(n) 的程式碼再巢狀迴圈一遍,它的時間複雜度就是 O(n²) 了。
立方階O(n³)、K次方階O(n^k)
參考上面的O(n²) 去理解就好了,O(n³)相當於三層n迴圈,其它的類似。
三、空間複雜度計算
空間複雜度 O(1)
如果演算法執行所需要的臨時空間不隨著某個變數n的大小而變化,即此演算法空間複雜度為一個常量,可表示為 O(1)。
int i = 1; int j = 2; ++i; j++; int m = i + j;
程式碼中的 i、j、m 所分配的空間都不隨著處理資料量變化,因此它的空間複雜度 S(n) = O(1)。
空間複雜度 O(n)
int[] m = new int[n] for(i = 1; i <= n; ++i) { j = i; j++; }
這段程式碼中,第一行new了一個數組出來,這個資料佔用的大小為n,後面雖然有迴圈,但沒有再分配新的空間,因此,這段程式碼的空間複雜度主要看第一行即可,即 S(n) = O(n)。
總結
評價一個演算法的效率主要是看它的時間複雜度和空間複雜度情況。可能有的開發者接觸時間複雜度和空間複雜度的優化不太多(尤其是客戶端),但在服務端的應用是比較廣泛的,在巨大併發量的情況下,小部分時間複雜度或空間複雜度上的優化都能帶來巨大的效能提升,是非常有必要了解的。
————————————————
原文連結:https://blog.csdn.net/haha223545/java/article/details/93619874