演算法 | 選擇排序 | 氣泡排序 | 合併排序 | 遞迴

更新於 2024/10/13閱讀時間約 11 分鐘

上回提到,演算法是一種解決問題的方法。光是簡單的將數字有小排到大就有很多種不同的排序演算法可以選擇。這次,我們來介紹幾個常見的排序演算法,看看它們是怎麼運作的。

選擇排序(Selection Sort):

我們先來看一個問題:如何把 [3, 1, 4, 2] 這組數字從小排到大?

選擇排序的邏輯很簡單:程式會先從頭到尾「掃描」一遍所有數字,找到當前範圍內最小的數字,然後把它和最左邊的數字交換 (swap)。接著,再從剩下的數字中找出最小值,重複這個過程,直到整個數列都排好為止。

示範程式

raw-image

具體步驟:

  1. 一開始,程式會檢查整個數列 [3, 1, 4, 2],發現最小的數字是 1,然後把它和最左邊的 3 交換。交換後的結果是 [1, 3, 4, 2]。(補充:每次掃描到最小值,會先暫存在記憶體,例如一開始掃到 3 先存,但掃到 1 又替換成最小值是 1。
  2. 接著,程式會只看剩下的部分 [3, 4, 2],發現最小的數字是 2,再把它和當前最左邊的 3 交換。這次交換後,數列變成 [1, 2, 4, 3]
  3. 然後,再看剩下的 [4, 3],最小值是 3,把它和 4 交換,最後得到排序完成的數列 [1, 2, 3, 4]

小結:

選擇排序的運作方式其實很直觀,簡單來說就是不斷找最小值,再用交換的方式換到正確的位置。即使中間有數字已經是正確的順序,演算法還是會繼續完整地掃描整個數列,直到所有數字都排好。

計算選擇排序的步驟:

假設長度為 n 的數列,選擇排序的步驟如下:

  • 第一次掃描:需要掃描所有 n−1 個數字,找到最小值。
  • 第二次掃描:需要掃描剩下的 n−2 個數字,找到最小值。
  • 第三次掃描:掃描剩下的 n−3 個數字,依此類推……

所以,總共的比較次數可以表示為:

(n−1)+(n−2)+(n−3)+⋯+1

這其實是一個等差數列的總和,數學上的表示為:

raw-image

選擇排序的 Big O 為 O(n2),其實不是效率很高的演算法。忘記 Big O 是什麼的朋友,請看上一篇解說。

演算法 | Big O 複雜度| Pseudocode 偽代碼

選擇排序偽代碼:

for i from 0 to n-1:
find smallet number between numbers[i] and numbers[n-1] // 從第0項到最後一項
swap smallet number with numbers[i] // 找到最小,就跟當前的最左邊交換


氣泡排序(Bubble Sort)

氣泡排序是比較相鄰的兩個數字。如果前一個比後一個大,就交換位置。我猜應該是感覺就像氣泡啵啵啵,故命名為氣泡排序:

氣泡排序的特點:

氣泡排序每次都會讓最大的數到正確的位置,如果沒有任何交換的過程,就可以提前結束程式。例如 [1, 2, 3, 4] 就不用一直比。

我們還是用之前的例子 [3, 1, 4, 2],來看氣泡排序的具體過程。

具體步驟:

  1. 第一輪:程式會從數列的第一個數字開始,依次比較相鄰的兩個數字。如果前一個比後一個大,就交換位置:
    • 3 和 1 比較,因為 3 > 1,所以交換,結果變成 [1, 3, 4, 2]
    • 接著比較 3 和 4,因為 3 < 4,所以不交換。
    • 最後比較 4 和 2,因為 4 > 2,交換,結果變成 [1, 3, 2, 4]
  2. 第二輪:我們重複這個步驟,但這次可以忽略最後一個數字4),因為它已經是最大的數了:
    • 1 和 3 比較,因為 1 < 3,不交換。
    • 然後比較 3 和 2,因為 3 > 2,所以交換,結果變成 [1, 2, 3, 4]
  3. 第三輪:這時候,只剩下前兩個數字需要比較:
    • 1 和 2 比較,因為 1 < 2,不需要交換。

這樣,數列就已經完全排序好了,最終結果是 [1, 2, 3, 4]

挑戰題:寫出偽代碼

i from n-1 to 1:  
j from 0 to i-1:
if j > j+1:
swap j and j+1

在氣泡排序中,會用到 i 跟 j 的迴圈。本題的目標是將 [5, 4, 6, 0, 1] 這個數列由小排到大。第一次我們先將 i 定在最右邊,而 j 從第一個元素開始比較。

第一輪比較 ( i=n-1 ):

  • 起始點在數字 5,現在比較 5 和它旁邊的 4
  • 因為 4 比較小,所以交換兩個數字。
  • 數列變為:[4, 5, 6, 0, 1]

接下來,我們繼續使用 j 比較相鄰的元素。

  • 比較 5656 小,所以不交換,數列保持不變:[4, 5, 6, 0, 1]
  • 再比較 6006 小,所以交換。
  • 數列變為:[4, 5, 0, 6, 1]
  • 最後比較 6116 小,再次交換。
  • 數列變為:[4, 5, 0, 1, 6]

第一輪結果:

到此為止,最大的數字 6 已經「冒泡」到了最後一位(最右邊)。下一輪就不用再比到6。

第一輪結果圖示。

第一輪結果圖示。

第二輪比較:

  • i 移到倒數第二位,j 繼續從頭開始比較。
  • 比較 45,因為 45 小,不交換。
  • 接著比較 50,因為 05 小,所以交換。
  • 數列變為:[4, 0, 5, 1, 6]
  • 再比較 51,因為 15 小,再次交換。
  • 數列變為:[4, 0, 1, 5, 6]

現在,第二大的數字 5 也移到了它的正確位置。

只比 4051。

只比 4051。

第三輪比較:

  • i 再移到倒數第三位,繼續從頭比較。
  • 比較 40,因為 04 小,交換。
  • 數列變為:[0, 4, 1, 5, 6]
  • 接著比較 41,因為 14 小,再次交換。
  • 數列變為:[0, 1, 4, 5, 6]

到此為止,第三大的數字 4 也移到了正確的位置。

raw-image


第四輪比較:

  • 現在只剩下最後兩個數字需要比較。
  • 比較 01,因為 01 小,不需要交換。
raw-image



合併排序(Merge sort ) 與遞迴(Recursion)

把一個大問題分成很多小問題來解決,最後再把小問題的結果組合起來,解決原來的大問題。

  • 把數列切對半:不斷地將數列分成兩半,直到每一部分只剩下一個元素。
  • 排序並合併:當每一部分都分開成單個元素後,開始逐步將它們合併,並在合併的過程中把數字排好。

這裡的概念可以參考 CS50 的影片,會更清楚地理解。

也可以用這個網站玩玩看。

以下是圖示:

Java Program for Merge Sort - geeksforgeeks

Java Program for Merge Sort - geeksforgeeks

步驟 1: 分割數列

合併排序首先會將數列不斷地對半分割,直到每個部分只剩一個元素。因為圖示清楚,這部分我就不多論述。

步驟 2: 開始合併並排序

接下來,我們開始進行合併,並在合併過程中對數字進行排序。

第一次合併:

  • 合併 [53][13],因為 13 < 53,結果是 [13, 53]
  • 合併 [43][10],因為 10 < 43,結果是 [10, 43]

第二次合併:(注意這部分的合併)

  • 最後將兩個已排序的部分 [13, 53][10, 43] 進行合併:
    • 比較 13 和 10,因為 10 < 13,所以結果先放 10。
    • 再比較 13 和 43,因為 13 < 43,所以結果放 13。
    • 然後比較 53 和 43,因為 43 < 53,結果放 43。
    • 最後只剩下 53,直接放進結果。
    • 合併結果是 [10, 13, 43, 53]

最終結果:

排序完成的數列是:[10, 13, 43, 53]

在合併排序的過程中,可能會使用到遞迴的概念,以下介紹遞迴的概念。

遞迴(Recursion)

遞迴就是一種程式自己呼叫自己的方法。在遞迴中,一個函式(或方法)會不斷地呼叫自己,直到滿足一個特定的「中止條件」,如果沒有中止條件會使程式當掉。

遞迴是一個效能不好的方式,所以在合併排序中通常也會使用迴圈(loop)的方式來取代遞迴,這種方式不需要不斷呼叫自己,而是通過反覆執行來完成任務。

以下示範用遞迴「持續往左切一半,直到只剩下一個數字」。

raw-image

那什麼時候停止呢?就是當切到只剩一個就會回傳該數字。

raw-image


動態規劃、貪婪演算法觀念

除了以上的排序,還有其他的演算法。我來簡單介紹這兩個演算法的重點,若要再更細節可以再搜尋其他大神的文章。

1. 動態規劃 (Dynamic Programming)

動態規劃的重點在於 記住已經計算過的結果,先存在暫時的地方。這樣在未來如果再遇到相同的問題,就不用重新計算,節省時間。這個過程叫做 記憶化 (Memoization)

舉例:找最短路徑

假設你在迷宮裡,要從起點走到終點。我們需要找到一條最短的路,避開障礙物並選擇最快的路徑。

  • 當走到另一個分岔點時,如果需要知道從之前走過的位置到終點的距離,就可以直接使用之前記住的結果,不用重算
  • 當走到每個新位置時,可以根據已經計算好的結果來做出最好的選擇,節省時間。
  • 以下圖來說,電腦會先記憶 A 到 B 的距離、A 到 C 的距離,如果詢問 A 到 D 的最短路徑,它就會根據加總的距離最小,給出答案為 A-C-D。
raw-image

2. 貪婪演算法 (Greedy Algorithm)

貪婪演算法的特點是 每次只考慮當下的最好選擇,但它不會去想將來的情況。因此,貪婪演算法可以眼下最近的例子,但是以全局來說,可能不是最佳解。

舉例:找最短路徑

再來看看你在迷宮裡找最短路徑的例子。

  • 如果你用貪婪演算法,當你站在一個分岔路口時,你只會選擇看起來最短的路,而不會考慮其他可能會更好但看起來比較遠的路。
  • 以下圖來看,A 要到 D ,邏輯是向右選擇途徑。出發先看到最短距離是 C (1),走到 C 後最短距離是到 B (3),再向右 7 格到 D。但實際上最短的距離應該是 A C D 才對。
raw-image


如果你對這些演算法有興趣,或者有什麼問題,歡迎在下方留言!讓我們一起學習、討論~ 👩‍💻👨‍💻

留言0
查看全部
avatar-img
發表第一個留言支持創作者!
演算法是一種解決問題的虛擬邏輯,他不像 C 語言有直接的程式碼,而是一種虛擬的問題解決方式。 想像一下,今天要在字典裡面找到 Zoo,有幾種方法: 逐頁查找:如果字典有 1000 頁,最糟情況下需要翻 1000 次 才能找到。 兩頁兩頁找:這樣的話,1000 頁最多要翻 500 次。 二分查
在上一篇文章中,我們介紹了「陣列」的基本使用方式。本篇將帶你深入探討 C 語言中字串的運作原理,了解如何以陣列形式儲存字串。此外,我們還會介紹如何將英文字母透過 ASCII 表轉換成數值,並說明其在電腦中的實際應用。最後,解析 Command Line Argument(命令列參數)的使用方法。
程式並不是寫完就可以自動運行,還需要經過「編譯」的階段。此篇文章會介紹編譯的四個階段,更貼近電腦科學。另外,也說明「陣列」的資料儲存模式跟實際的運用,提供優化程式的建議,最後再分享三個除錯的技巧。
本文深入探討程式設計中的迴圈概念,介紹三種常見的迴圈使用方式:for、while 和 do...while。透過實例如計算 1 到 100 的總和以及九九乘法表,讓讀者能夠理解這些迴圈的應用場景及其運作邏輯。文章強調掌握迴圈及函數的重要性,並提供了避免常見錯誤的提示,非常適合初學者。
為什麼從C開始? 為什麼不是python? 因為 C 是個「麻煩」的程式語言,而python 其實已經簡化很多步驟。先學C語言了解程式語言的架構,再學其他語言就會覺得更簡單! 我是搭配 CS50 的免費課程,看完影片還是不太懂,可以再搭配我的筆記複習。釐清基礎架構對電腦科學更有概念,可以更精準
在我們深入探討程式設計之前,讓我們先掌握 Linux 作業系統的基礎,學習如何在命令提示字元(CMD)中靈活運用指令,並了解位元與位元組之間的差異。這樣的學習路徑雖然乏味但有助於打下穩固的基礎,一起在電腦新手村獲得經驗值吧!
演算法是一種解決問題的虛擬邏輯,他不像 C 語言有直接的程式碼,而是一種虛擬的問題解決方式。 想像一下,今天要在字典裡面找到 Zoo,有幾種方法: 逐頁查找:如果字典有 1000 頁,最糟情況下需要翻 1000 次 才能找到。 兩頁兩頁找:這樣的話,1000 頁最多要翻 500 次。 二分查
在上一篇文章中,我們介紹了「陣列」的基本使用方式。本篇將帶你深入探討 C 語言中字串的運作原理,了解如何以陣列形式儲存字串。此外,我們還會介紹如何將英文字母透過 ASCII 表轉換成數值,並說明其在電腦中的實際應用。最後,解析 Command Line Argument(命令列參數)的使用方法。
程式並不是寫完就可以自動運行,還需要經過「編譯」的階段。此篇文章會介紹編譯的四個階段,更貼近電腦科學。另外,也說明「陣列」的資料儲存模式跟實際的運用,提供優化程式的建議,最後再分享三個除錯的技巧。
本文深入探討程式設計中的迴圈概念,介紹三種常見的迴圈使用方式:for、while 和 do...while。透過實例如計算 1 到 100 的總和以及九九乘法表,讓讀者能夠理解這些迴圈的應用場景及其運作邏輯。文章強調掌握迴圈及函數的重要性,並提供了避免常見錯誤的提示,非常適合初學者。
為什麼從C開始? 為什麼不是python? 因為 C 是個「麻煩」的程式語言,而python 其實已經簡化很多步驟。先學C語言了解程式語言的架構,再學其他語言就會覺得更簡單! 我是搭配 CS50 的免費課程,看完影片還是不太懂,可以再搭配我的筆記複習。釐清基礎架構對電腦科學更有概念,可以更精準
在我們深入探討程式設計之前,讓我們先掌握 Linux 作業系統的基礎,學習如何在命令提示字元(CMD)中靈活運用指令,並了解位元與位元組之間的差異。這樣的學習路徑雖然乏味但有助於打下穩固的基礎,一起在電腦新手村獲得經驗值吧!
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
※ 什麼是ORDER BY? 可以讓SELECT出來的結果,根據你想要的方式排序。簡單說,用於對查詢結果進行排序。 ※ 語法: SELECT select_list FROM table_name ORDER BY column1 [ASC|DESC], column2 [ASC|DESC]
Thumbnail
在進行SQL查詢邏輯更改時,需要適當地使用SubQuery和join來達到新的排序需求。本文將介紹原本的撈取邏輯、需求以及如何使用SubQuery來解決新的排序需求。
Thumbnail
題目敘述 Sort Colors 給定一個色彩陣列,裡面的顏色包含0紅色,1白色,2藍色。 要求我們透過in-place操作,把色彩陣列依序從左到右排好, 依序出現的是紅色、白色、藍色。
描述順序的方式有很多種,可以用數字標示文字的方式,如下 文字 文字 文字 用數字標示文字的方式我覺得有個特質是,文字重要的程度是遞減的,也就是數字從一往後會越來越不重要。這個現象是我在寫這篇文的當下感覺到的,可能是我在過去經驗聽過有人說越重要的東西要先擺在前面;又或者是我在高中以前的
Thumbnail
解決電腦上遇到的問題、證明正確性、探討效率 並且很著重溝通,說服別人你做的事是正確且有效率的。 內容: 計算模型、資料結構介紹、演算法介紹、時間複雜度介紹。
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
※ 什麼是ORDER BY? 可以讓SELECT出來的結果,根據你想要的方式排序。簡單說,用於對查詢結果進行排序。 ※ 語法: SELECT select_list FROM table_name ORDER BY column1 [ASC|DESC], column2 [ASC|DESC]
Thumbnail
在進行SQL查詢邏輯更改時,需要適當地使用SubQuery和join來達到新的排序需求。本文將介紹原本的撈取邏輯、需求以及如何使用SubQuery來解決新的排序需求。
Thumbnail
題目敘述 Sort Colors 給定一個色彩陣列,裡面的顏色包含0紅色,1白色,2藍色。 要求我們透過in-place操作,把色彩陣列依序從左到右排好, 依序出現的是紅色、白色、藍色。
描述順序的方式有很多種,可以用數字標示文字的方式,如下 文字 文字 文字 用數字標示文字的方式我覺得有個特質是,文字重要的程度是遞減的,也就是數字從一往後會越來越不重要。這個現象是我在寫這篇文的當下感覺到的,可能是我在過去經驗聽過有人說越重要的東西要先擺在前面;又或者是我在高中以前的
Thumbnail
解決電腦上遇到的問題、證明正確性、探討效率 並且很著重溝通,說服別人你做的事是正確且有效率的。 內容: 計算模型、資料結構介紹、演算法介紹、時間複雜度介紹。