統測資電類・資訊科專業科目(二)核心考點

第 10 章:排序演算法

本章統整統測程式設計中「排序」的核心工具:泡沫排序、選擇排序、比較次數計算,以及交換函式必須用指標才能真正交換的關鍵觀念(111~115 年共計 2 題考古真題)。 出題老師最愛把「非標準寫法的雙層迴圈排序追蹤」與「交換函式的引數型態不符」揉合在一起設計陷阱。 本教學將帶你掌握逐輪手寫追蹤的紀律,並透過2 題真題 + 5 題模擬題的所有選項深度剖析,徹底掃除所有答題盲點!

🔃
6 大核心
排序演算法觀念全解構
🎯
2 題真題
111~115 統測題全收錄
💡
全選項剖析
A/B/C/D 逐項深入拆解
🚀
100% 互動
點擊即答 + 展開詳解

1 泡沫排序(Bubble Sort)基本邏輯:相鄰比較與交換

泡沫排序是最基礎的排序法:每一輪都掃過整個(或剩餘)陣列,只要相鄰兩個元素順序不對就交換,反覆進行直到整個陣列排序完成。跑完 n-1 輪之後,最大的幾個元素就會像泡泡一樣依序「浮」到陣列尾端。

C Code:完整的泡沫排序(bubble sort)函式
#include <stdio.h>
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
int main(void) {
    int arr[5] = {5, 2, 4, 1, 3};
    for (int i = 0; i < 5 - 1; i++)
        for (int j = 0; j < 5 - 1 - i; j++)
            if (arr[j] > arr[j + 1])
                swap(&arr[j], &arr[j + 1]);
    for (int i = 0; i < 5; i++) printf("%d ", arr[i]); // 1 2 3 4 5
    printf("\n");
    return 0;
}
🖥️ 終端機執行輸出結果
1 2 3 4 5
💡 白話比喻:泡泡浮上水面

想像每一輪都是「相鄰兩人比身高,矮的往前站、高的往後站」,一輪比下來,全場最高的那個人一定會被擠到隊伍最後面——這就是「最大值像泡泡一樣浮到陣列尾端」的意思。跑滿 n-1 輪,就能保證所有人都排好隊。

2 選擇排序(Selection Sort):對照另一種排序策略

選擇排序每一輪都先「選出」剩餘範圍中最小的元素,再跟目前位置交換一次;跟泡沫排序不同,選擇排序每一輪只交換一次(泡沫排序每一輪可能交換很多次)。

C Code:選擇排序(selection sort)
#include <stdio.h>
int main(void) {
    int arr[5] = {5, 2, 4, 1, 3};
    for (int i = 0; i < 5; i++) {
        int min = i;
        for (int j = i + 1; j < 5; j++)
            if (arr[j] < arr[min]) min = j;
        int t = arr[i]; arr[i] = arr[min]; arr[min] = t;
    }
    for (int i = 0; i < 5; i++) printf("%d ", arr[i]); // 1 2 3 4 5
    printf("\n");
    return 0;
}
比較項目泡沫排序選擇排序
每一輪動作相鄰兩兩比較,順序不對就交換掃描找出最小值,只跟目前位置交換一次
每一輪交換次數可能很多次最多1次
共同點都需要外層+內層兩層迴圈,跑完後陣列排序完成

3 排序演算法的比較次數:n(n-1)/2 的由來

對 n 筆資料進行標準泡沫排序或選擇排序,兩兩比較的總次數是一個等差數列求和:第1輪比較 (n-1) 次,第2輪比較 (n-2) 次……最後一輪比較1次,加總起來就是 n(n-1)/2。

輪次比較次數
第1輪n-1 次
第2輪n-2 次
…………
最後一輪1 次
總計(n-1)+(n-2)+...+1 = n(n-1)/2
🧠 常見計算陷阱

算出 n(n-1) 之後別忘了除以2——如果沒有除以2,等於把每一對資料重複算了兩次(一次算A跟B比、一次又算B跟A比,但這其實是同一次比較)。也別誤以為每一輪都要固定比較n次(忽略了範圍會隨排序進行逐輪縮小)。

4 交換函式必須用指標,傳值無法真正交換

交換兩個數值的函式若用一般變數(傳值)當參數,函式內的交換只會影響函式內的複本,不會真的改到呼叫端的陣列;必須改用指標(傳址)才能真正交換原始資料。

C Code:傳值 swap 無效 vs 傳指標 swap 有效
void swap_wrong(int a, int b) { int t=a; a=b; b=t; }        // 傳值:呼叫端不會被真的交換
void swap_right(int *a, int *b) { int t=*a; *a=*b; *b=t; }  // 傳指標:才能真的交換
⚠️ 統測常考陷阱:呼叫時傳入的引數型態要跟函式參數型態一致

如果 swap() 的參數宣告是一般 int,呼叫時卻傳入一個指標運算式(例如 numbers+i,型態是 int*),編譯器會直接報型態不符的錯誤(expected 'int' but argument is of type 'int *'),這不是邏輯錯誤,而是編譯期就會抓到的型態不匹配錯誤。

5 追蹤複雜的雙層迴圈排序邏輯:逐輪手寫陣列狀態

統測有些排序追蹤題,寫法跟教科書上標準的泡沫排序或選擇排序不完全一樣(例如比較與交換的範圍、時機被稍微改寫過)。遇到這種題目,務必逐行手寫每一輪比較與交換後的陣列狀態,不要用直覺猜答案——直覺很容易被「看起來像」某種標準排序法給誤導。

步驟陣列狀態這一步做了什麼
初始[5, 2, 4]—
比較①[2, 5, 4]5>2,交換位置0、1
比較②[2, 4, 5]5>4,交換位置1、2
💡 追蹤技巧:先看清楚「比較的是誰跟誰」,再看「交換的時機」

先確認每一次 if 判斷式比較的究竟是「相鄰兩個」還是「當下位置跟目前找到的最小值」,再確認交換是「符合條件就立刻交換」還是「掃完整個範圍才交換一次」——這兩種設計的最終結果可能差異很大,務必依實際程式碼逐步模擬,不要套用自己以為的「標準寫法」。

6 常考陷阱總複習:型態不符的函式呼叫錯誤

排序題常會考「把兩兩交換的程式碼改寫成呼叫一個 swap() 函式」,這時最容易出現的錯誤,就是呼叫時傳入的引數型態跟函式參數宣告的型態不一致。

C Code:呼叫時傳入指標運算式,卻宣告成一般 int 參數(會編譯錯誤)
void swap(int a, int b){  // 參數是int,要求傳值
    int tmp;
    tmp=a; a=b; b=tmp;
}
// ......
swap(numbers+i, numbers+min);  // ❌ numbers+i 是 int*,跟參數要求的int型態不符
⚠️ 檢查清單:函式呼叫型態不符的三個徵兆

1. 引數裡出現了 陣列名稱+索引 這種指標算術運算式,但函式參數宣告的是一般型態(不是指標)。
2. 錯誤訊息明確提到「expected 'int' but argument is of type 'int *'」這類型態不符的字樣。
3. 想要函式真正交換陣列裡的資料,卻沒有把函式參數宣告成指標型態。

7 111~115 歷屆統測真題全選項深度剖析(共 2 題)

以下收錄統測專業科目(二)近年全部 2 題排序演算法真題。每題均提供高解析度原始考題掃描圖、互動選項按鈕與所有選項逐項深入解析:

113 統測專二 Q47 📌 概念:陣列/排序演算法追蹤(持續交換至最小值)
試題來源:技專校院入學測驗中心
曉華寫了泡沫排序演算法對N個整數排序,其中字元'a'的ASCII碼為97,程式輸出結果為何?
題幹程式碼
#include <stdio.h>
#define N 11
void swap(int a, int b){
    int tmp;
    tmp=a;
    a=b;
    b=tmp;
}
void main(void){
    int numbers[N]={1,3,5,7,9,2,4,6,8,0,'a'};
    int tmp, i, min;
    for(min=0; min<N; min++)
        for(i=0; i<N; i++){
            if(numbers[i]<numbers[min]){
                tmp=numbers[min];
                numbers[min]=numbers[i];
                numbers[i]=tmp;
            }
        }
    for(i=0; i<N; i++){
      printf("%d ", numbers[i]);
    }
}
113 統測專二 Q47 題目截圖
本題標準答案: (C) 97 9 8 7 6 5 4 3 2 1 0

🔍 四個選項逐項深度剖析

選項 A 錯誤解析
這個答案的排列方向(97放最前面、其餘遞減排列)是對的,但把字元'a'直接當成字元本身輸出,而不是它在陣列中真正儲存的整數值97。因為陣列宣告為 int numbers[N],'a'這個字元常值存入int陣列時會被轉換成它的ASCII碼97(一個普通整數),程式用 %d 格式化輸出,印出來的會是97這個數字,不會是字元'a'本身。
選項 B 錯誤解析
這個答案把排序方向弄反了(誤以為是遞增排序),也同樣誤把97當成字元'a'輸出。這個雙層迴圈演算法「持續尋找更小值、立刻交換到min位置」的機制,實際效果是把陣列排成遞減順序,不是遞增。
正確答案完整推導(★ 本題標準答案)
陣列初始值('a'轉成int是97)為[1,3,5,7,9,2,4,6,8,0,97]。這個雙層迴圈的寫法是:對每個min,內層i掃描整個陣列,只要 numbers[i]<numbers[min] 就立刻交換,這個「持續尋找更小值、立刻交換到min位置」的機制,會逐步把當下找到的最小值往前擠到min這個位置,其餘的值依序被擠成遞減排列。實際模擬全部11輪,最終陣列變為[97,9,8,7,6,5,4,3,2,1,0],選 (C)。
選項 D 錯誤解析
排序方向錯誤(誤以為遞增排序),且把97放到了最後面而不是排序後遞減順序中該有的最前端位置——由於97是陣列中最大的值,遞減排序後理應排在陣列最前面(索引0),不會被放到最後。
113 統測專二 Q48 📌 概念:函式呼叫/參數傳遞(指標與整數型態不符)
試題來源:技專校院入學測驗中心
曉華把交換程式碼改寫成swap()函式呼叫,結果程式無法執行並出現錯誤訊息 expected 'int' but argument is of type 'int *',錯誤原因為何?
題幹程式碼
void swap(int a, int b){
    int tmp;
    tmp=a; a=b; b=tmp;
}
void main(void){
    int numbers[N]={1,3,5,7,9,2,4,6,8,0,'a'};
    int tmp, i, min;
    for(min=0; min<N; min++)
        for(i=0; i<N; i++){
            if(numbers[i]<numbers[min]){
                swap(numbers+i, numbers+min);   // 啟用此行呼叫
            }
        }
    for(i=0; i<N; i++)
      printf("%d ", numbers[i]);
}
113 統測專二 Q48 題目截圖
本題標準答案: (A) 呼叫swap()時,使用的引數資料型態與副程式不一致

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
swap() 函式參數宣告為 void swap(int a, int b),要求傳入 int(一般整數值,傳值呼叫);但呼叫時傳入的 numbers+i、numbers+min 是指標算術運算的結果,型態是 int*(指向int的指標),跟函式參數要求的 int 型態不符,編譯器才會報出 expected 'int' but argument is of type 'int *' 這個型態不匹配的錯誤,選 (A)。
選項 B 錯誤解析
numbers 是陣列名稱,在運算式中會退化成指標,指標加上一個整數(numbers+i)是完全合法的指標算術,用來取得陣列中第i個元素的位址,這個語法本身並沒有錯誤,錯誤訊息指出的問題也不是「不能相加」,而是傳入函式時的型態不符。
選項 C 錯誤解析
陣列宣告中出現的字元常值 'a'(值97,是陣列的其中一個初始值)跟 swap() 函式參數名稱 a 是完全不同層級的東西(一個是字元常值,一個是變數名稱),兩者不會產生任何命名衝突,這跟編譯器實際回報的型態不符錯誤訊息也完全對不上。
選項 D 錯誤解析
min 在外層 for(min=0; min<N; min++) 中會被明確賦值後才使用,並沒有「沒有初始值」的問題;而且編譯器回報的錯誤訊息明確是關於引數型態不符(int 與 int*),跟 min 有沒有初始值無關。

8 高職段考與模擬精選實戰題(5 題全選項解析)

透過以下 5 題精選模擬試題,全面檢驗你對泡沫排序、選擇排序、比較次數與傳值/傳指標交換的掌握度:

模擬實戰第 1 題 📌 概念:標準泡沫排序追蹤
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
int arr[4] = {4, 1, 3, 2};
for (int i = 0; i < 3; i++)
    for (int j = 0; j < 3 - i; j++)
        if (arr[j] > arr[j+1]) {
            int t = arr[j]; arr[j] = arr[j+1]; arr[j+1] = t;
        }
printf("%d %d %d %d", arr[0], arr[1], arr[2], arr[3]);
本題標準答案: (A) 1 2 3 4

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
這是標準的泡沫排序,相鄰元素若前者比後者大就交換,跑完外層3輪後,陣列會被排成由小到大的遞增順序。逐輪追蹤:第1輪後{1,4,3,2}→{1,3,4,2}→{1,3,2,4};第2輪後{1,3,2,4}→{1,2,3,4};第3輪確認順序已排好。最終為{1,2,3,4},選 (A)。
選項 B(4 3 2 1)錯誤解析
這是遞減排序的結果,但程式碼裡的交換條件是 arr[j] > arr[j+1](前面比後面大才交換,讓較大的值持續往後移),這會產生遞增排序,不是遞減排序。
選項 C(4 1 3 2)錯誤解析
這是完全沒有排序、維持原始輸入順序的結果,忽略了程式碼中雙層迴圈與if交換條件確實會執行多次比較與交換的動作。
選項 D(2 1 3 4)錯誤解析
這個答案只排序了一部分,可能是漏算了某幾輪的比較與交換,沒有讓迴圈跑完所有必要的輪次,泡沫排序需要完整跑滿外層迴圈的輪數,才能保證整個陣列排序完成。
模擬實戰第 2 題 📌 概念:標準選擇排序追蹤
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
int arr[4] = {30, 10, 40, 20};
for (int i = 0; i < 4; i++) {
    int min = i;
    for (int j = i+1; j < 4; j++)
        if (arr[j] < arr[min]) min = j;
    int t = arr[i]; arr[i] = arr[min]; arr[min] = t;
}
printf("%d %d %d %d", arr[0], arr[1], arr[2], arr[3]);
本題標準答案: (A) 10 20 30 40

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
i=0:在索引0~3中找到最小值10(在索引1),交換arr[0]和arr[1]→{10,30,40,20}。i=1:在索引1~3中找到最小值20(在索引3),交換arr[1]和arr[3]→{10,20,40,30}。i=2:在索引2~3中找到最小值30(在索引3),交換arr[2]和arr[3]→{10,20,30,40}。i=3:只剩一個元素,不需交換。最終為{10,20,30,40},選 (A)。
選項 B(10 30 40 20)錯誤解析
只完成了第一輪「把最小值10換到最前面」的動作,之後的迴圈似乎沒有繼續執行,忽略了選擇排序需要對每個位置i都重複「在剩餘範圍找最小值並交換」的動作,不是只做一輪就結束。
選項 C(40 30 20 10)錯誤解析
這是遞減排序的結果,但程式碼裡尋找最小值的條件是 arr[j] < arr[min](找剩餘範圍中最小的),每輪選出最小值放到前面,會產生遞增排序,不是遞減排序。
選項 D(30 10 40 20)錯誤解析
這是完全沒有排序、維持原始輸入順序的結果,忽略了選擇排序迴圈確實會執行「尋找最小值」與「交換」的動作。
模擬實戰第 3 題 📌 概念:排序比較次數公式 n(n-1)/2
對n=6筆資料進行標準泡沫排序(或選擇排序),總共需要幾次兩兩比較?
本題標準答案: (B) 15

🔍 四個選項逐項深度剖析

選項 A(6)錯誤解析
這個答案誤以為比較次數等於資料筆數本身,忽略了排序需要「每一筆資料都跟其他每一筆資料兩兩比較」,比較的總次數遠不只等於資料筆數n。
正確答案完整推導(★ 本題標準答案)
標準的泡沫排序或選擇排序,對n筆資料排序時的兩兩比較總次數是一個等差數列求和:第1輪比較(n-1)次,第2輪比較(n-2)次,……最後一輪比較1次,總和為 (n-1)+(n-2)+...+1 = n(n-1)/2。n=6時,比較次數為 6×5/2=15,選 (B)。
選項 C(30)錯誤解析
這個答案可能是把 n(n-1) 算出來了(6×5=30)卻忘記除以2,也就是把每一對資料重複算了兩次(一次算A跟B比、一次又算B跟A比,但實際上這是同一次比較,不該算兩次)。
選項 D(36)錯誤解析
這個答案很接近n²=36,可能誤以為每一輪都要跟全部n筆資料比較(而不是隨著排序進行、範圍逐輪縮小),忽略了排序演算法每一輪需要比較的範圍會隨已排序完成的部分增加而遞減,不是固定n次。
模擬實戰第 4 題 📌 概念:傳值交換函式無法真正交換
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
void swap_bad(int a, int b) { int t = a; a = b; b = t; }
int main(void) {
    int x = 1, y = 2;
    swap_bad(x, y);
    printf("%d %d", x, y);
    return 0;
}
本題標準答案: (A) 1 2

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
swap_bad() 的參數a、b都是一般int型態,屬於傳值呼叫,呼叫時只是把x、y的值複製一份給a、b,函式內對a、b的交換只發生在這兩個複本上,完全不會影響main()裡的原始變數x、y,所以x、y維持呼叫前的值,輸出"1 2",選 (A)。
選項 B(2 1)錯誤解析
此答案誤以為函式內對參數a、b的交換會反映回呼叫端的x、y,這只有在參數宣告為指標型態並用&x, &y呼叫時才會發生(也就是傳址呼叫)。本題swap_bad()用的是一般int參數,屬於傳值呼叫,x、y不會被真正交換。
選項 C(0 0)錯誤解析
此答案誤以為呼叫函式後,原本的x、y會被清空或重置,但傳值呼叫只是複製一份值給函式使用,不會對呼叫端的原始變數做任何清空或重置的動作。
選項 D 錯誤解析
這段程式碼的語法完全正確,函式參數用一般int型態、呼叫時直接傳入變數x、y都是合法的C語法,不會有編譯錯誤(只是無法達到「真正交換」的效果而已,這是邏輯上的問題,不是語法錯誤)。
模擬實戰第 5 題 📌 概念:正確的傳指標交換函式追蹤
已知下列 C 語言程式碼片段,執行後印出的內容為何?
C Code
void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }
int main(void) {
    int x = 5, y = 9;
    swap(&x, &y);
    printf("%d %d", x, y);
    return 0;
}
本題標準答案: (A) 9 5

🔍 四個選項逐項深度剖析

正確答案完整推導(★ 本題標準答案)
swap() 的參數宣告為指標型態 int *a, int *b,呼叫時傳入 &x、&y,屬於傳址呼叫。函式內用 *a、*b 解參考,直接存取並交換x、y實際的記憶體內容,是真正的交換,執行後x變成原本y的值9,y變成原本x的值5,輸出"9 5",選 (A)。
選項 B(5 9)錯誤解析
此答案誤以為交換沒有真正生效,維持了呼叫前的原始值。但本題使用的是正確的傳址呼叫(指標參數+&取址呼叫),函式內的交換確實會真正修改到main()裡的x、y。
選項 C(5 5)錯誤解析
此答案誤以為兩個變數都被設成了其中一個的值,但swap()函式用暫存變數t先保存*a(x的值),再把*b(y的值)指定給*a,最後把t(x的舊值)指定給*b,這是標準的三步驟交換,x跟y最終應該各自持有對方原本的值,不會兩個變得相同。
選項 D(9 9)錯誤解析
同樣誤以為兩個變數最終變得相同,忽略了swap()函式用暫存變數t正確保留了x的舊值,確保x最終能拿到y的舊值、而y也能拿到x的舊值,兩者不會相同。

9 考前衝刺 10 秒速記口訣 & 核心知識檢核清單

✅
泡沫排序 vs 選擇排序
泡沫:相鄰兩兩比較、每輪可能多次交換;選擇:找最小值、每輪只交換一次。
✅
比較次數公式
n筆資料兩兩比較總次數是n(n-1)/2,別忘了除以2,也別誤用固定n次估算。
✅
交換必須用指標
swap函式參數若是一般int(傳值),呼叫端資料不會被真正交換,要用指標參數。
✅
引數型態要與函式參數一致
傳入陣列指標運算式(如numbers+i)給宣告成int的參數,會直接編譯錯誤。
✅
非標準排序題務必逐輪手寫
比較與交換的範圍、時機若跟教科書寫法不同,要逐步模擬,不要用直覺猜答案。