---
# System prepended metadata

title: 時間複雜度
tags: [Notes, algorithm, Big-Oh]

---

###### tags: `Notes` `Big-Oh` `algorithm`
# 時間複雜度

## [速查表](https://kknews.cc/zh-tw/tech/l3y4qng.html)
## [初學者學演算法｜談什麼是演算法和時間複雜度](https://medium.com/appworks-school/%E5%88%9D%E5%AD%B8%E8%80%85%E5%AD%B8%E6%BC%94%E7%AE%97%E6%B3%95-%E8%AB%87%E4%BB%80%E9%BA%BC%E6%98%AF%E6%BC%94%E7%AE%97%E6%B3%95%E5%92%8C%E6%99%82%E9%96%93%E8%A4%87%E9%9B%9C%E5%BA%A6-b1f6908e4b80)

### 演算法
演算法的簡單定義式：輸入 + 演算法 = 輸出
![](https://miro.medium.com/max/1000/1*85eZc1aesbrPA3Rj55dVHw.jpeg)

### 時間複雜度
大 O 符號是用來描述一個演算法在輸入 n 個東西時，總執行時間與 n 的關係。

![](https://miro.medium.com/max/700/1*LiDirYGz4qCHDflA_j0zsA.jpeg)

![](https://miro.medium.com/max/700/1*MYETv-_QFW2hMBAWbvnKAw.jpeg)

## [初學者學演算法｜從時間複雜度認識常見演算法](https://medium.com/appworks-school/%E5%88%9D%E5%AD%B8%E8%80%85%E5%AD%B8%E6%BC%94%E7%AE%97%E6%B3%95-%E6%8E%92%E5%BA%8F%E6%B3%95%E5%85%A5%E9%96%80-%E9%81%B8%E6%93%87%E6%8E%92%E5%BA%8F%E8%88%87%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E6%B3%95-23d4bc7085ff)
### 常見的六種時間複雜度與演算法
|Big Oh|演算法|
|-|:-:|
|O(1)|陣列讀取|
|O(n)|簡易搜尋|
|O(log n)|二分搜尋|
|O(n²)|選擇排序法<Br>插入排序法|
|O(n logn)|合併排序法|
|O(2^n)|費波那契數列|

### O(1)：陣列讀取
時間複雜度為 O(1) 的演算法，代表著不管你輸入多少個東西，程式都會在同一個時間跑完。
在程式設計中，最簡單的例子就是讀取一個陣列中特定索引值的元素。

![](https://miro.medium.com/max/2000/1*x6F0ENO7N8GsqHfxf-80OA.jpeg)

```python=
Pokemons = ["卡丘","胖丁","尼龜","比獸","呆獸","種子","小剛"]

print(Pokemons[n])
>> "卡丘"
```

### O(n)：簡易搜尋
時間複雜度為 O(n) 的演算法，代表著執行步驟會跟著輸入 n 等比例的增加。
例如當 n = 8，程式就會在 8 個步驟完成。最簡單的例子，就是所謂的簡易搜尋。

```python=
Pokemons = ["卡丘","胖丁","尼龜","比獸","呆獸","種子","小剛"]
for Pokemon in Pokemons:
  if Pokemon == "呆獸":
    print("找到呆獸！")
    break
  else:
    print("這個櫃子裡不是呆獸")
```

### O(log n)：二分搜尋法
a.k.a 終極密碼搜尋法

![](https://miro.medium.com/max/2000/1*gxrCcIKY4JNouQkM4JlQww.jpeg)

## [初學者學演算法｜排序法入門：選擇排序與插入排序法](https://medium.com/appworks-school/%E5%88%9D%E5%AD%B8%E8%80%85%E5%AD%B8%E6%BC%94%E7%AE%97%E6%B3%95-%E6%8E%92%E5%BA%8F%E6%B3%95%E5%85%A5%E9%96%80-%E9%81%B8%E6%93%87%E6%8E%92%E5%BA%8F%E8%88%87%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E6%B3%95-23d4bc7085ff)
### O(n²)：選擇排序法 (Selection Sort)
時間複雜度為 O(n²) 的演算法，代表著執行步驟會跟著輸入 n 成次方比例的增加。最基礎的排序法之一：選擇排序法(Selection Sort) 是 O(n²) 複雜度的代表。

- **找最小值** :point_right: ==n==
從「未排序好的數字」中找到最小值
- **丟到左邊** :point_right:（n+(n-1)+(n-2)+…+1) = ==n * (n+1) / 2== 
把最小值丟到「未排序好的數字」的最左邊，把它標示成已排序好

假設有一個 [41, 33, 17, 80, 61, 5, 55] 的陣列，我們用圖的例子來一步一步理解選擇排序是如何進行，在下面的圖中，我們把尚未排序好的數字用紅色標示，這輪找到的最小值以橘色標示，排序好的以藍色標示。
![](https://miro.medium.com/max/1400/1*MUEvL8qTjbRtz22PlTSXPA.jpeg)

- **結合找到最小值與丟到最左邊**
把上面的兩個結果加起來，(n * (n+1) / 2) + n = n * (n+3) / 2。回想一下我們在這篇提到的，時間複雜度只取最高次方項並省略所有係數，因此雖然選擇排序的步驟數是 n * (n+3) / 2，其時間複雜度我們還是會記為 O(n²)。

### O(n²)：插入排序法 (Insertion Sort)
同樣擁有 O(n²) 時間複雜度，插入排序法 Insertion Sort 則是另外一個非常常見的排序法。簡單來說，插入排序法就是你玩撲克牌時用到的排序
- 讀一個數字
從「未排序過的數字」中讀取一個數
- 插入合適位置
把這個讀取的數往前插入一個位置

現在，我們重新使用 [41, 33, 17, 80, 61, 5, 55] 的陣列，在下面的圖中，我們把尚未排序過的數字用紅色標示，這輪要插入的值以橘色標示，排序過的以藍色標示。
![](https://miro.medium.com/max/1400/1*b4zbvCTViTymTzSH9qtDUw.jpeg)

- **讀一個數字與結合插入合適位置**

我們在「插入合適位置」需要 1+2+3+…+n 個步驟、在「讀一個數字」需要總共 n 個步驟，計算後 就會得到和選擇排序法相同的 n * (n+3) / 2 個步驟，其時間複雜度我們記為 O(n²)。

### O(n logn)：合併排序 (Merge Sort)
時間複雜度為 O(n log n) 的演算法，代表著執行時間會隨著以二為底的 log n 再乘上 n 成長。最常見的例子是合併排序法 (Merge Sort) 與快速排序法 (Quick Sort)，而本篇文章將以新手比較好掌握的合併排序法為例。

![](https://miro.medium.com/max/2000/1*lbHMJmGtjb_qe943kE_bmg.jpeg)

- 每回合的合併要一個一個比對，需要花：O(n)
- 每回合都縮減一半，所以總共需要回合數：O(log n)
- 所以總共是 O(n log n)

## [初學者學演算法｜從費氏數列認識何謂遞迴](https://medium.com/appworks-school/%E5%88%9D%E5%AD%B8%E8%80%85%E5%AD%B8%E6%BC%94%E7%AE%97%E6%B3%95-%E5%BE%9E%E8%B2%BB%E6%B0%8F%E6%95%B8%E5%88%97%E8%AA%8D%E8%AD%98%E4%BD%95%E8%AC%82%E9%81%9E%E8%BF%B4-dea15d2808a3)

### O(2^n)：費波那契數列 (Fibonacci numbers)
時間複雜度為 O(2^n) 的演算法，代表著執行步驟會是 2 的 n 次方。實務上來說，這樣的執行效率非常的慢，例如當輸入為 100 時，執行步驟就會暴增到 30 位數，等於即使每秒都能執行 1 百億個步驟，也需要個天荒地老 1 千億年才能完成。因此，這樣的時間複雜度是大部分工程師在設計演算法時想要避免的。
而這樣的時間複雜度，最常見的例子是以遞迴計算費波那契數列 (Fibonacci numbers)。
![](https://miro.medium.com/max/1400/1*GYlkF7G60gbe1B951GulYw.jpeg)
```python
計算費波那契數列第 10 項的程式碼如下：
def fibo(n):
    if n == 0:
        return 0;
    elif n == 1:
        return 1;
    return fibo(n-1) + fibo(n-2)
​
print(fibo(10))
```

- 每往下一層，步驟數變 2 倍
- 總共有 n 層
- 時間複雜度：O(2^n)


### 同場加映：如何加速計算費氏數列的演算法
在上面我們提到過，時間複雜度 O(2^n)，在實務上非常的慢。因此，我們就要來試著改善上面的演算法，看看能不能讓他加速一點。

我們可以「用空間換取時間」，把每一個算過的值都用一個大表依序記下來。這樣當我們想要知道 F(3) 是多少時，我不用重新計算 F(2)+F(1)，只要去大表上找到 F(3) 的答案就好。讓我們以計算 F(10) 為例：
```python
table = [0]*3
table[0] = 0
table[1] = 1
​
for i in range(2,11):
    table[2] = table[1] + table[0]
    table[0] = table[1]
    table[1] = table[2]
​
print(table[2])
```

算每一項的同時我們需要去讀取前兩項的值，並把它加起來，總共需要三個步驟。而我們總共需要算 n-1 次，所以總共的步驟數是 3(n-1) 次，只看最高次方項並拔掉係數後，我們就可以得到加速後的時間複雜度：O(n)

## Bubble Sort、Bucket Sort
### O(n²)：氣泡排序法 (Bubble Sort)
氣泡排序法(Bubble Sort)是最容易理解和實作的一種排序演算法，也翻譯作冒泡排序法。由於它很容易學習，所以也是許多演算法課程中第一個學習的排序演算法。
- 逐步比較第 n 個位置和第 n+1 個位置元素的大小
![](https://pic.pimg.tw/emn178/1352552991-3385741824.png)

總共需要進行 n 個回合，每個回合進行 n-i 次。故時間複雜度為O(n²)

### O(n²)：桶排序法 (Bucket Sort)
桶排序法會將資料先分類丟進不同的桶子中，
接著再將所有桶子內的數列以==插入排序==等適合**少量**資料的演算法進行排序之後，
再依照桶子的順序把桶子中的元素串接在一起，如此一來就能完成所有排序。
![](https://4.bp.blogspot.com/-a_kcbOcjY8Q/VLgatONWASI/AAAAAAAAAJk/lwVXIqvvsN8/s1600/sort.png)

- 最差情況:
當所有的元素都在同一個桶中，時間複雜度為桶內的排序演算法速度 O(n²)
- 最優情況:
元素完全分散在 m 個不同的桶子中，完全不需進行排序。O(n + m)




## 附件

### 資料結構
![](https://i1.kknews.cc/SIG=3ucccfk/ctp-vzntr/p992sr28n9sn4q25o1o7489r27s64514.jpg)

### 排序
![](https://i1.kknews.cc/SIG=2vj3eid/ctp-vzntr/110q26q1455748n1npq3p7oon77336po.jpg)

## 圖形
![](https://i1.kknews.cc/SIG=2mh198c/ctp-vzntr/33667q68173q405rno4p3q178q3776s0.jpg)

## Heap
![](https://i1.kknews.cc/SIG=3js1sf6/ctp-vzntr/n52sq12341324rq49814p9p1o25oq21s.jpg)