程式的「快」與「慢」:全面解析演算法的時間複雜度 (O, Ω, Θ)

更新 發佈閱讀 13 分鐘

你是否曾經想過,為什麼 Google 搜尋能在 0.5 秒內篩選全世界的資料,而學校的選課系統卻在 1000 人同時上線時就立刻崩潰?

這不是魔法,也不是電腦硬體的絕對差異。這背後的秘密,是「策略」的勝利。在電腦科學中,我們稱這個解決問題的策略為「演算法 (Algorithm)」。而用來衡量一個策略「好」或「壞」的尺,就是我們今天要深入探討的「時間複雜度 (Time Complexity)」。

這篇文章會帶你學會看懂程式設計師的「秘密語言」。讀完後,你將能理解為什麼有些程式快如閃電,有些卻慢如龜爬。我們將會認識三位主角:悲觀的 Big O、樂觀的 Big Omega (Ω),以及務實的 Big Theta (Θ)。

什麼是「效率」?不是「秒」,而是「步驟」

在深入之前,我們必須先打破一個常見的迷思。當我們說一個演算法「快」,我們指的不是「它跑完花了多少秒」。

想像一下,你和一位奧運短跑冠軍比賽「寄信」。比賽內容是將 1000 封信送到城市裡的 1000 個不同地址。你騎著摩托車,而短跑冠軍用跑的。一開始,寄 1 封信,他肯定比你快。寄 10 封信,他可能還是比你快。但當信件數量增加到 1000 封時,你騎車的「策略」 ($O(N)$) 絕對會勝過他跑步的「策略」 (也是 O(N),但他前面的常數太高)。如果比賽內容改成「每送一封信,都要先跟前面送過的每一家打招呼」,那他的工作量會變成 N^2,他可能一輩子都送不完。

這就是關鍵:硬體(跑多快)會影響「秒數」,但策略(演算法)會影響「工作量的成長速度」。一個好的演算法,就算跑在 20 年前的舊電腦上,當資料量一大,最終還是會勝過跑在超級電腦上的爛演算法。

因此,電腦科學家決定用一種更公平的方式來衡量效率:「執行步驟數」與「問題規模 (N)」之間的關係。「問題規模 (N)」可以是你需要排序的數字個數、圖書館裡的書本總數,或是選課系統的學生人數。「執行步驟數」就是電腦需要做的基本操作,例如「比較兩個數字」、「讀取一本書的標題」等等。時間複雜度,就是在研究:當 N 逐漸變大時,執行步驟數會如何膨脹?

悲觀的保證:Big O (上界)

在演算法的世界裡,最常被掛在嘴邊的就是「Big O」。Big O 的 O,是 "Order of"(...的量級)的意思。它代表的是「上界 (Upper Bound)」,也就是我們對一個演算法所做的「最壞情況的保證」。

想像一下,你在一個有 N 本書的書架上找一本特定的書。你採用的「演算法」是線性搜索 (Linear Search):從第一本開始,一本一本往後看。最壞的情況是什麼?就是你要找的書在最後一個位置,或是這本書根本不在書架上。在這種情況下,你必須看完 N 本書才能得出結論。因此,我們說這個演算法的 Big O 是 O(N)(讀作 "Big-O of N")。

O(N) 這句話的真正意思是:「這個演算法的執行步驟,在最壞的情況下,其成長速度不會快過 N。」這就像一個悲觀的工程師在打包票:「你放心,不管發生什麼事,工作量最多就是 N 這個等級,不會更慘了。」這是一個非常強大的「保證」。

在分析 Big O 時,我們只關心「主要矛盾」。假設一個演算法精確的步驟數是 T(N) = 3N^2 + 100N + 5000。當 N 變得非常大(例如 N = 100 萬)時,N^2 (1 兆) 這一項會變得無比巨大,後面 $100N (1 億) 和 $5000$ 根本不值一提。我們甚至不在乎 N^2 前面那個 3 倍的常數,因為 N^2 的「成長形態」才是決定性的。因此,我們抓大放小,說這個演算法的複雜度就是 O(N^2)。

演算法的「武力排行」

根據 Big O,我們可以把常見的演算法效率排成一個「家族」:

  1. O(1) (常數時間):最快的等級。神諭。不管書架上有 10 本還是 10 億本書,你總能「立刻」知道書在哪裡(例如:直接從書的「索引編號」取出)。你的步驟數永遠是固定的,與 N 無關。
  2. O(log N) (對數時間):快到不可思議的等級。查字典(二分搜索法)。你要在 1000 頁的字典裡找一個字。你翻到中間第 500 頁,發現要找的字在後面,於是你丟掉前面 500 頁。接著你翻 750 頁... 你每一步都能排除掉「一半」的錯誤答案。N = 10 億時,你大概也只需要 30 步就能找到。這是 Google 搜尋等的核心魔法之一。
  3. O(N) (線性時間):公平且良好的等級。排隊。N = 1000 人,服務 1000 個人。N 變成 2 倍,花費時間也變成 2 倍。這就是我們剛才的「線性搜索」。
  4. O(N log N) (線性對數時間):非常優秀的等級。比喻:專業的排序。這是電腦「排序」大量資料(例如:把 100 萬個數字由小到大排好)能做到的理論上最好的效率。它比 O(N) 慢,但比 O(N^2) 快得多。
  5. O(N^2) (平方時間):危險的等級。全班同學(N 人)互相交換名片。每個人都要給 N-1 個人名片,總共的工作量接近 times N。這就是為什麼學校選課系統(N = 2000 人)如果採用 O(N^2) 的演算法,工作量會是 2000 x 2000 = 400 萬步。當 N 變成 3000 人,工作量就暴增到 900 萬步。N 增加 1.5 倍,工作量增加 2.25 倍。這就是系統崩潰的元兇。
  6. O(2^N) (指數時間):災難的等級。暴力破解密碼。N 是密碼長度。N 每增加 1(例如 4 位數密碼變 5 位數),你要嘗試的工作量就加倍。只要 N 大概超過 50,用全世界的電腦算到宇宙毀滅都算不完。

Big O 告訴我們的是「最壞的可能」,它讓我們能安心地睡覺,知道程式效能不會突破這個「天花板」。

樂觀的底線:Big Omega (Ω) (下界)

如果 Big O 是悲觀主義者,那 Big Omega (唸作 "Bi-g Oh-May-Ga") 就是樂觀主義者。Ω 符號代表的是「下界 (Lower Bound)」,也就是「最好情況的保證」。

Ω 回答的問題是:「這個演算法最少需要多少步驟?」或者說:「無論你運氣多好,工作量絕對不可能低於這個底線。」

讓我們再次回到在 N 本書的書架上找書的「線性搜索」演算法。最好的情況是什麼?就是你運氣爆棚,要找的書剛好在第一個位置!你只看 1 本書就找到了。在這種情況下,你的步驟數是 1。因此,我們說線性搜索的 Big Omega 是 Ω(1)(常數時間)。

所以,對於「線性搜索」這個演算法,我們有了一個更完整的描述:

  • 最壞情況 (Big O):O(N)
  • 最好情況 (Big Omega): Ω(1)

這兩個符號一起告訴我們,這個演算法的執行效率,會落在 1 和 N 這個「區間」之內。

你可能會想,Ω 好像不太實用?畢竟,誰會關心「最好情況」?老闆只會問你「最晚什麼時候能做完」(Big O),而不會問你「運氣最好的話,最快能多快」(Big Omega)。這在很大程度上是事實,這也是為什麼 Big O 在業界遠比 Ω常用。但是,Ω 在學術分析上依然有其重要性。它幫助我們定義一個「問題」本身的固有難度。

例如,「在 N 個亂序的數字中找到最大值」這個問題,你最好的運氣(Ω)是什麼?你必須把 N 個數字都看過一遍,才能確定哪個是最大的。因此,這個問題本身的下界就是 Ω(N)。這也意味著,如果有人宣稱他發明了一個 O(log N) 的演算法來解決這個問題,他一定是搞錯了,因為他連「最少」該做的工作Ω(N)都沒做到。

務實的標尺:Big Theta (Θ) (緊束界)

我們現在有了「天花板 (O)」和「地板 (Ω)」。但如果一個演算法的「天花板」和「地板」剛好在同一個位置呢?這時,我們就有了最精確的描述:Big Theta (唸作 "Bi-g Thay-Ta")。

Θ 代表的是「緊束界 (Tight Bound)」。當一個演算法的最壞情況 (O) 與最好情況 (Θ) 的成長速度相同時,我們就使用 Θ 來描述它。

如果一個演算法是 Θ(N^2),這句話的意思是:

  1. 它最壞不會壞過 N^2 O(N^2)
  2. 它最好也不會好過 N^2 Ω(N^2)

白話文就是:「這個演算法,不管運氣好還是運氣壞,它的工作量都死死地卡在 $N^2$ 這個等級。」

讓我們來看一個完美的 Θ 範例。任務是:「在一個有 N 個數字的陣列中,找出最大值」。你唯一的演算法就是:從第一個數字開始,一路看到最後一個,並隨時記住你目前看過的「最大值」。

  • 最壞情況:數字是「1, 2, 3, 4, 5... N」(一路遞增)。你每次都要更新你的「最大值」,總共看了 N 個數字。複雜度是 O(N)。
  • 最好情況:數字是「N, N-1, ... 3, 2, 1」(一路遞減)。你第一個數字 N 就是最大值,後面雖然都不用更新,但你還是必須把 N 個數字全部看完,才能 100% 確定 N 是最大的。複雜度是 Ω(N)。

看到了嗎?這個「找最大值」演算法,它的最壞情況 O(N) 和最好情況 Ω(N) 是一樣的!因此,我們給它一個最精確的稱號:Θ (Theta-N)。

一場排序的最終對決

讓我們用一個經典的「排序」問題,來總結 O, Ω, Θ 的應用。假設你要將 N 張撲克牌由小到大排好。

策略一:冒泡排序 (Bubble Sort)

這是一種古老且效率差的排法。它不斷地從頭比較「相鄰兩張牌」,如果順序錯了就交換。

  • 最壞情況 (O):牌是完全逆序的(K, Q, J... 2, A)。你需要跑 N 趟,每趟都要比較 N 次,工作量是 O(N^2)。
  • 最好情況 (Omega):牌已經排好了。你聰明地跑第一趟時,發現「咦?完全沒有任何交換發生」,於是你提前停止。你只跑了一趟。工作量是 $\Omega(N)$。
  • 結論:由於 O(N^2) 和 Ω(N) 不相等,我們不能用 Θ 來描述它。我們只能說,這個演算法的效率介於 Ω(N) 和 O(N^2) 之間。

策略二:合併排序 (Merge Sort)

這是一種非常聰明且高效的「分治法」。它先把 N 張牌「切成兩半」,再把那兩半「各自切半」,直到你手上只剩 N 堆「一張牌」(單張牌一定是排好的)。然後,你再把這些小堆「兩兩合併」成排好序的牌堆,直到最後合併回一大堆。

  • 最壞情況 (O):它不管牌是怎樣的,都「必須」執行完整的「切開」和「合併」流程。這個流程的數學分析是 O(N log N)。
  • 最好情況 (Omega):就算牌一開始就排好了,它還是會「傻傻地」把它們全切開,再全合併回去。它沒有辦法「偷懶」。所以它的最好情況也是 Omega(N log N)。
  • 結論:由於 O(N log N) 和 Omega(N log N) 相等,我們終於可以使用 Theta!「合併排序」是一個Theta(N log N) 的演算法。它非常穩定,無論輸入是好是壞,它的效率都維持在 N log N 等級。

結語:不只是理論,更是策略的藝術

我們今天學會了三種衡量演算法效率的「尺」:

  • Big O (上界):悲觀的保證。「最壞不會超過這裡」。
  • Big Ω (下界):樂觀的底線。「最好也不會低於這裡」。
  • Big Θ (緊束界):務實的判決。「它的效率就卡死在這裡」。

在業界,99% 的人談論的都是 Big O,因為它提供了「最壞情況」的保證,這在建造大型系統時至關重要。但理解 Ω 和 Θ,能讓你更深刻地洞悉一個演算法的「真實本質」。

這套「秘密語言」一點都不神秘。它只是在描述「成長的趨勢」。下次當你遇到一個卡頓的應用程式,或是讚嘆 Google 搜尋的急速時,你會知道,這背後不是魔法,而是工程師在 O(N^2) 和 O(log N) 之間,做出了一個決定性的策略選擇。


留言
avatar-img
留言分享你的想法!
avatar-img
資訊科學的小筆記
0會員
17內容數
我上AI相關課程的心得
2025/11/05
這篇文章透過急診室的生動比喻,詳細解釋了優先佇列(Priority Queue, PQ)的概念、與傳統佇列(Queue)的差異,以及為何其重要性在電腦科學中無所不在。文章逐步剖析了使用未排序陣列、已排序陣列及自平衡二元搜尋樹(BST)來實作優先佇列的優缺點,引導讀者認識到堆疊(Heap)結構必要性。
2025/11/05
這篇文章透過急診室的生動比喻,詳細解釋了優先佇列(Priority Queue, PQ)的概念、與傳統佇列(Queue)的差異,以及為何其重要性在電腦科學中無所不在。文章逐步剖析了使用未排序陣列、已排序陣列及自平衡二元搜尋樹(BST)來實作優先佇列的優缺點,引導讀者認識到堆疊(Heap)結構必要性。
2025/11/05
本文深入探討了三種重要的平衡搜尋樹結構:AVL 樹、紅黑樹和 B 樹。首先介紹了 AVL 樹作為效率標竿,強調其嚴格的平衡機制與 O(log n) 的搜尋、插入、刪除時間複雜度。接著引入紅黑樹,作為 AVL 樹的務實妥協,討論其透過顏色屬性而非絕對高度來維持平衡,使其在插入和刪除操作上成本更低。
2025/11/05
本文深入探討了三種重要的平衡搜尋樹結構:AVL 樹、紅黑樹和 B 樹。首先介紹了 AVL 樹作為效率標竿,強調其嚴格的平衡機制與 O(log n) 的搜尋、插入、刪除時間複雜度。接著引入紅黑樹,作為 AVL 樹的務實妥協,討論其透過顏色屬性而非絕對高度來維持平衡,使其在插入和刪除操作上成本更低。
2025/11/05
這篇文章深入探討了二元搜尋樹 (BST) 的效能瓶頸,特別是在資料依序插入時會退化成鏈結串列的 O(n) 複雜度問題。文章接著介紹了 AVL 樹,這是歷史上第一個自平衡二元搜尋樹,由 Adelson-Velsky 和 Landis 發明。
2025/11/05
這篇文章深入探討了二元搜尋樹 (BST) 的效能瓶頸,特別是在資料依序插入時會退化成鏈結串列的 O(n) 複雜度問題。文章接著介紹了 AVL 樹,這是歷史上第一個自平衡二元搜尋樹,由 Adelson-Velsky 和 Landis 發明。
看更多
你可能也想看
Thumbnail
搬家不只添購必需品,更能透過蝦皮分潤計畫賺取零用金!本文分享近期搬家時添購的各種實用好物,包含多功能工作桌、電競椅、氣炸烤箱、收納神器等,並詳述如何透過蝦皮雙 11 活動聰明購物、善用優惠,同時利用分潤機制將敗家行為轉化為被動收入,推薦給想聰明消費又想賺額外收入的你!
Thumbnail
搬家不只添購必需品,更能透過蝦皮分潤計畫賺取零用金!本文分享近期搬家時添購的各種實用好物,包含多功能工作桌、電競椅、氣炸烤箱、收納神器等,並詳述如何透過蝦皮雙 11 活動聰明購物、善用優惠,同時利用分潤機制將敗家行為轉化為被動收入,推薦給想聰明消費又想賺額外收入的你!
Thumbnail
貓奴每月進貢的時間又來啦! 身為專業貢品官,我從蝦皮搜尋各種零食,只為取悅家中三位貓主子!結果究竟會是龍心大悅,亦或是冷眼相待,就讓我們繼續看下去~
Thumbnail
貓奴每月進貢的時間又來啦! 身為專業貢品官,我從蝦皮搜尋各種零食,只為取悅家中三位貓主子!結果究竟會是龍心大悅,亦或是冷眼相待,就讓我們繼續看下去~
Thumbnail
期末考結束了!這學期我負責開了大二必修課:資料結構(Data Structure),雖然作業、小考都是由TA(課程助理)幫我批改,不過期中期末考都是我自己把所有的考卷改完的。總共大約是將近150份的考卷,有兩個班。而期末考的內容,其實基本上我把大部分的規則、程式碼都寫在題目裡面或者放在附錄讓同學可以
Thumbnail
期末考結束了!這學期我負責開了大二必修課:資料結構(Data Structure),雖然作業、小考都是由TA(課程助理)幫我批改,不過期中期末考都是我自己把所有的考卷改完的。總共大約是將近150份的考卷,有兩個班。而期末考的內容,其實基本上我把大部分的規則、程式碼都寫在題目裡面或者放在附錄讓同學可以
Thumbnail
請使用 C 或 Java 語言寫一副程式,此副程式對一個長度為 10 的整數陣列 A[0:9],最多花費 15 次的數值比較運算,尋找陣列中的最小值及最大值,並分別存入 Min 及 Max。(注意:請加註解說明程式碼寫法) 【103.關務】
Thumbnail
請使用 C 或 Java 語言寫一副程式,此副程式對一個長度為 10 的整數陣列 A[0:9],最多花費 15 次的數值比較運算,尋找陣列中的最小值及最大值,並分別存入 Min 及 Max。(注意:請加註解說明程式碼寫法) 【103.關務】
Thumbnail
這篇文章說明演算法的定義、特徵以及一個有趣的小知識。演算法被定義為解決問題的流程,並以機車故障為例說明。文章也列出演算法的五個特徵:輸入、有限性、明確性、有效性及輸出。最後,文章提及世界上公認的第一個演算法是歐幾裡德演算法。
Thumbnail
這篇文章說明演算法的定義、特徵以及一個有趣的小知識。演算法被定義為解決問題的流程,並以機車故障為例說明。文章也列出演算法的五個特徵:輸入、有限性、明確性、有效性及輸出。最後,文章提及世界上公認的第一個演算法是歐幾裡德演算法。
Thumbnail
上次我們提到了演算法(algorithm),它是一種解決問題的方式。但演算法只是資料結構與演算法(Data Structures and Algorithms, DSA)這個領域的一部分。今天,我們要進一步探索這個主題,了解它的核心概念。 什麼是資料結構與演算法呢?簡單來說,資料結構是用來組織和存
Thumbnail
上次我們提到了演算法(algorithm),它是一種解決問題的方式。但演算法只是資料結構與演算法(Data Structures and Algorithms, DSA)這個領域的一部分。今天,我們要進一步探索這個主題,了解它的核心概念。 什麼是資料結構與演算法呢?簡單來說,資料結構是用來組織和存
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了array之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了array之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list
Thumbnail
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
Thumbnail
從範例學python的目標讀者: 針對剛進入的初學者,想學習Python語言。 有基礎本數學邏輯基礎即可。 從小遊戲學python的目標讀者: 針對已經有經驗的C/C++, Python, 或其他有程式基礎的讀者。 想實作一些小專案,從實做中學習如何分析需求、元件分拆、到底層實作
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
本文將深入探討鏈表的核心概念,使用 JavaScript 來說明如何實現和操作鏈表(Linked List),包括 append、prepend、remove、find 和 reverse 等五大方法。
Thumbnail
本文將深入探討鏈表的核心概念,使用 JavaScript 來說明如何實現和操作鏈表(Linked List),包括 append、prepend、remove、find 和 reverse 等五大方法。
Thumbnail
這道題你一定會解,但你知道怎麼把迴圈改寫成「迭代」嗎?這招學起來!
Thumbnail
這道題你一定會解,但你知道怎麼把迴圈改寫成「迭代」嗎?這招學起來!
Thumbnail
演算法學習路徑圖 這次我們將精確定位出,在整個演算法學習中,我們所站立的位置;了解資料結構與演算法的定義後,拿到我們在這個世界中的方位,就能「有根有據」的展開學習路徑,建立「系統架構化」的知識體系。 不論學習什麼,明確的「定位」出自己在整個學習藍圖中的位置,是非常重要的。軟體世界要學的何其多,光是
Thumbnail
演算法學習路徑圖 這次我們將精確定位出,在整個演算法學習中,我們所站立的位置;了解資料結構與演算法的定義後,拿到我們在這個世界中的方位,就能「有根有據」的展開學習路徑,建立「系統架構化」的知識體系。 不論學習什麼,明確的「定位」出自己在整個學習藍圖中的位置,是非常重要的。軟體世界要學的何其多,光是
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News