搜尋與排序 search and sort - csie.ntu.edu.twr95116/ca200/slide/c9_sortsearch.pdf ·...
TRANSCRIPT
排序與搜尋 搜尋
循序(線性)搜尋法(Linear Search)
二分搜尋法(Binary Search)
排序
氣泡排序法(Bubble Sort)
選擇排序法(Selection Sort)
插入排序法(Insertion Sort)
快速排序法(Quick Sort)
2
選擇排序法 Selection Sort 在一段資料中找出最大(小)值後,才做交換
範例程式碼(將資料由小排到大)
4
int i, j, tmp, min; int n[5] = {7,1,3,9,5}; // 欲排序的資料
for( i=0; i<4; i++) {min = i;for( j=i+1; j<=4; j++) // 找出最小值
if(n[ j] < n[min]) min = j;
// 把最小值跟第 i 個做交換if (min != i) {
tmp = n[i];n[i] = n[min];n[min] = tmp;
} }
7 1 3 9 5
i=0, j=2, min是1, n[1]=1
7 1 3 9 5
i=0, j=3 , min是1, n[1]=1
7 1 3 9 5
i=0, j=4 , min是1, n[1]=1
1 7 3 9 5
i=0, j=4 , 把min換到最前面
7 1 3 9 5
i=0, j=1, min是1, n[1]=1
1 7 3 9 5
i=1, j=2
1
1
1
1
1
7
min
已排好的 目前的
……
i=0, min是0, n[0]=7
操作範例
選擇排序法 Selection Sort
特點
當資料紀錄很大 (欄位數多) ,比較之鍵值是少量的,則適合Selection Sort 因為每個回合至多一次
交換動作
5
7 1 3 9 5
1 7 3 9 5
1 3 7 9 5
1 3 5 9 7
1 3 5 7 9
課堂練習 以下為一個動態的”選擇排序法”程式,請用迴圈
讓使用者輸入6次數字,然後將每次排序的結果輸出:
輸出輸入範例如下:
第一次輸入:9;輸出:9
第二次輸入:3;輸出:3 9
第三次輸入:7;輸出:3 7 9
第四次輸入:5;輸出:3 5 7 9
第五次輸入:9;輸出:3 5 7 9 9
第六次輸入:2;輸出:2 3 5 7 9 9
程式結束
6
Ans: 9_SelectionSort.cpp
插入排序法 Insertion Sort 將一段資料中最右(左)邊的資料當作key,然後往左(右)
塞入此資料中作排序 範例程式碼(將資料由小排到大)
7
int i, j, key; int n[5] = {7,1,3,9,5}; // 欲排序的資料
for( i=1; i<=4; i++) {key=n[i];for( j=i-1; j>=0 && n[ j]>key; j--)
n[ j+1] = n[ j];
n[ j+1] = key;}
1 7 3 9 5
i=2, j=1
1 3 7 9 5
i=2, j=0
1 3 7 9 5
i=3, j=2, 9比7大,不必排
1 3 7 9 5
i=4, j=3
7 1 3 9 5
i=1, j=0
1 3 5 7 9
i=4, j=1
已排好的 目前的
……
課堂練習 以下為一個動態的”插入排序法”程式,請用迴圈
讓使用者輸入6次數字,然後將每次排序的結果輸出:
輸出輸入範例如下:
第一次輸入:9;輸出:9
第二次輸入:3;輸出:3 9
第三次輸入:7;輸出:3 7 9
第四次輸入:5;輸出:3 5 7 9
第五次輸入:9;輸出:3 5 7 9 9
第六次輸入:2;輸出:2 3 5 7 9 9
程式結束
8
Ans: 9_InsertionSort.cpp
操作範例 (1/2)
10 7 13 5 1
↓
比較10> 7 ,10和7對調
7 10 13 5 1
↓
比較10<= 13 ,不做任何事
7 10 13 5 1
↓
比較13>5 , 13和5對調
10
↓
比較13> 1 ,13和1對掉
7 10 5 13 1
最後結果: 最大的數跑到最右邊了
7 10 5 1
↓
比較7<=10 ,不做任何事
13
剩下4個數還未排序,重複剛才的動作,這次讓10跑到右邊
操作範例 (2/2)
7 10 5 1 13
11
完整操作範例• 10,7,13,5,1 由小到大排列
• Step1.1 10>7,於是兩者交換,陣列變成7,10,13,5,1
• Step1.2 10<13,於是沒有動作
• Step1.3 13>5,於是兩者交換,陣列變成7,10,5,13,1
• Step1.4 13>1,於是兩者交換,陣列變成7,10,5,1,13
• 到此為止,我們已經把最大的元素13移至最右端了,下一次只需檢查到陣列倒數第二個元素即可。
• Step2.1 7<10,於是沒有動作
• Step2.2 10>5,於是兩者交換,陣列變成7,5,10,1,13
• Step2.3 10>1,於是兩者交換,陣列變成7,5,1,10,13
• 第二大的元素10也就定位了。
• Step3.1 7>5,於是兩者交換,陣列變成5,7,1,10,13
• Step3.2 7>1,於是兩者交換,陣列變成5,1,7,10,13
• 第三大的元素7也就定位了。
• Step4.1 5>1,於是兩者交換,陣列變成1,5,7,10,13
• 完成。
12
快速排序法 Quick Sort (1/4)
平均情況下執行時間最快的方法
採取 Divide and Conquer 策略
觀念1
每回合處理之後,pk會被放置正確位子
15
1 2 3 … n
Pivot Key
1 2 3 … n
Pivot Key比Pivot Key小 比Pivot Key大
Divide!
快速排序法 Quick Sort (2/4)
觀念2
分為左右半部後 各自做遞迴 quick sort
如何決定 pk 之正確位置?
作法
16
Conquer!
1 2 3 4 5 6 7 8 9 10
26 5 37 1 61 11 59 48 39 65
i : 找>=pk j : 找<=pk
i jj: 找到 11 <= 26i:找到37>=26
swap
快速排序法 Quick Sort (3/4)
資料結構與C++程式設計進階班17
1 2 3 4 5 6 7 8 9 10
26 5 11 1 61 37 59 48 39 65
j: 找到 1 <= 26
i:找到61>=26 i 和 j 擦身而過,所以pk正確位置應該是4
1 2 3 4 5 6 7 8 9 10
1 5 11 26 61 37 59 48 39 65
swap
快速排序法 Quick Sort (4/4)
演算法:
最好與平均情況
nlogn
最差情況
n2
發生在資料由小到大或由大到小的情況
資料結構與C++程式設計進階班18
i : 找>=pk
j : 找<=pk
sort左半邊
sort右半邊
排序法的比較 執行效率(Computational Complexity) 速度:排序一群資料所需的”比較”次數
例如:氣泡排序法排序 N 筆資料需比較次數為為 (N-1)x(N-2)/2
記憶體空間(Memory Usage) 排序一群資料所需要的記憶體空間
例如:快速排序法排序N筆資料通常需 logN 的額外記憶體空間
穩定性(Stability) 對於鍵值相同的資料其”原始順序”排序後能否被保留? If Yes:穩定
If No:不穩定
19
分類與比較一覽表 同樣排序 n 筆資料
Bubble Selection Insertion Quick
平均情形 O(n2) O(n2) O(n+d) O(nlogn)
最糟情形 O(n2) O(n2) O(n2) O(n2)
記憶體使用 O(1) O(1) O(1) O(logn)~O(n)
穩定性 Y Y Y N
備註d是相反資料(需交換)的
個數
20
排序與搜尋 搜尋
循序搜尋法(Linear Search)
二分搜尋法(Binary Search)
排序
氣泡排序法(Bubble Sort)
選擇排序法(Selection Sort)
插入排序法(Insertion Sort)
快速排序法(Quick Sort)
21
循序(線性)搜尋法 Linear Search 在一群資料中,從頭搜尋到尾直到找到資料為止
又稱 Sequential Search
範例程式碼int linearSearch(int key) {
int n[10] = {-2,0,1,2,3,4,6,7,9,14}, key=3; int found = -1;
for( i=0; i< 10; i++)if(n[i] == key) {
found=i; break;
}return found;
}22
• 實施之前,紀錄不需事先排序過• 可用隨機存取 (Array) 或循序存取 (Linked List) 方式支援
輸出1. 大 於 等 於 0
表示找到第i筆符合key值
2. 等於-1表示沒找到
二元搜尋法 Binary Search
在已排序的資料數列中尋找某一筆資料
須藉助隨機存取 (Array) 機制支援
在執行二元搜尋時的比較,可以分成三種情況,如下所示:
搜尋鍵值小於陣列的中間元素 鍵值在資料陣列的前半部
搜尋鍵值大於陣列的中間元素 鍵值在資料陣列的後半部
搜尋鍵值等於陣列的中間元素 找到搜尋的鍵值
23
小 → 大
中間元素
1 5 9 12 16Num
Num[0] Num[1] Num[2] Num[3] Num[4]
Up
↓
Low
↓
int SearchKey=7; //數字7在陣列裡的哪個位置 (0~4)
int Low=0,Up=4;
int Middle=(Up+Low)/2; // (0+4)/2 = 2
Middle
↓
操作範例 (1/4)
24
1 5 9 12 16Num
Num[0] Num[1] Num[2] Num[3] Num[4]
Up
↓
Low
↓
//因為: 7不在Num[middle], 且7<9
//所以: 我們知道7如果在陣列裡的話,應該會在middle 的左邊
// => 把up移到middle-1的位置 Up=Middle-1;
//把Up移到新的位置後,計算新的middle
middle=(Up+Low)/2; // middle = (0+1)/2 =0
Middle
↓
操作範例 (2/4)
25
1 5 9 12 16Num
Num[0] Num[1] Num[2] Num[3] Num[4]
Up
↓
Low
↓
//因為: 7 不在Num[middle], 且7>1
//所以: 我們知道7如果在陣列裡的話,應該會在middle 的右邊
// => 把Low移到middle+1的位置 Low=Middle+1;
//把Low移到新的位置後,計算新的middle
middle=(Up+Low)/2; // middle = (1+1)/2 =1
Middle
↓
操作範例 (3/4)
26
1 5 9 12 16Num
Num[0] Num[1] Num[2] Num[3] Num[4]
Up
↓
Low
↓
//因為: 7 不在Num[middle], 且7>5
//所以: 我們知道7如果在陣列裡的話,應該會在middle 的右邊
// => 把Low移到middle+1的位置 Low=Middle+1;
//把Low移到新的位置後,發現Low > Up
//告知使用者7不在陣列裡
Middle
↓
操作範例 (4/4)
27