1. 程式人生 > 實用技巧 >時間複雜度和空間複雜度學習

時間複雜度和空間複雜度學習

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符號表示法並不是用於來真實代表演算法的執行時間的,它是用來表示程式碼執行時間的增長變化趨勢的”。
所以它的時間複雜度其實是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