第二章 初級排序算法
1 總覽
1.1 排序算法的模板
public class Example {
public static void sort(Comparable[] a) { /*排序代碼*/ }
private static boolean less(Comparable v, Comparable w) { return v.compareTo(w)<0; }
private static void exch(Comparable[] a, int i, int j) {
Comparable t = a[i]; a[i] = a[j]; a[j] = t;
}
private static void show(Comparable[] a) {
for (Comparable c : a) {
System.out.print(c+" ");
}
System.out.println();
}
public static boolean isSorted(Comparable[] a) {
for (int i=0; i<a.length-1; i++) {
if (less(a[i], a[i+1])) {
return false;
}
}
return true;
}
public static void main(String[] args) {
Comparable[] a = ...; //data source
sort(a);
assert isSorted(a);
show(a);
}
}
1.2 排序算法
- 選擇排序
- 插入排序
- 希爾排序
- 歸併排序
- 快速排序
- 堆排序
- …
1.3 性能指標
交換, 比較 的次數.
2 初級排序算法
2.1 選擇排序
2.1.1 原理
在數組中找到最小的元素, 然後將其與第一個元素交換位置(, 如果是本身的話就和自己交換). 然後找到剩下元素中最小的元素, 將它與第二個元素交換位置. 如此反覆, 直到排序完成.
2.1.2 實現
public class Selection {
public static void sort(Comaprable[] a) {
int N = a.length;
for (int i=0; i<N; i++) {
int minIndex = i;
for (int j=i+1; j<N; j++) {
if less(a[j], a[i]) {
minIndex = j;
}
}
exch(a, i, j);
}
}
}
2.1.3 代價分析
交換次數 | 比較次數 |
---|---|
爲什麼訪問次數是 ?
對於第一個元素, 最多對比 次. 對於第二個元素, 最多對比 次, … , 對於第倒數第二個元素, 需要訪問 1 次.
2.2 插入排序
2.2.1 原理
插入排序的原理就和整理撲克牌一樣, 當抽了一張牌以後, 需要把它插入相應的位置, 這就意味着比當前大的所有 “牌” 都需要向右移動一個位置. 插入排序所需的時間取決於輸入中元素的初始順序.
2.2.2 實現
public class Insertion {
public static void sort(Comparable[] a) {
//將 a 按升序排序 (小->大)
int N = a.length;
for (int i=0; i<N; i++) {
//將 a[i] 插入到 a[i-1], a[i-2], ... , a[0] 中
for (int j=i; j>0 && less(a[j], a[j-1]); j--) {
exch(a, j, j-1);
}
}
}
}
2.2.3 代價分析
交換次數 | 比較次數 | |
---|---|---|
平均 | ||
最壞 | ||
最好 |
最壞情況: 倒序
數組 | 交換次數 (當前) | 比較次數 (當前) |
---|---|---|
5, 4, 3, 2, 1 | - | - |
4, 5, 3, 2, 1 | 1 | 1 |
3, 4, 5, 2, 1 | 2 | 2 |
2, 3, 4, 5, 1 | 3 | 3 |
1, 2, 3, 4, 5 | 4 | 4 |
總計 | 10 | 10 |
最好情況: 已經排序完成
數組 | 交換次數 (當前) | 比較次數 (當前) |
---|---|---|
1, 2, 3, 4, 5 | - | - |
1, 2, 3, 4, 5 | 0 | 1 |
1, 2, 3, 4, 5 | 0 | 1 |
1, 2, 3, 4, 5 | 0 | 1 |
1, 2, 3, 4, 5 | 0 | 1 |
總計 | 0 | 4 |
2.2.4 部分有序
2.2.4.1 定義
倒置: 數組中的兩個順序顛倒的元素.
- E X A M P L E 中, 有 E-A, X-A, X-M, X-P, X-L, X-E, M-L, M-E, P-L, P-E, L-E 這些倒置.
部分有序: 如果數組中倒置的數量小於數組大小的某個倍數, 那麼我們說這個數組是 部分有序 的.
2.2.4.2 典型的部分有序數組
- 數組中每個元素距離它的最終位置都不遠;
- 一個有序的大數組接一個小數組;
- 數組中只有幾個元素的位置不確定.
2.2.4.3 事實
插入排序對部分有序的數組很有效. 當倒置的數量很少時, 插入排序很可能比其他任何算法都要快.
2.3 希爾排序
2.3.1 原理
是基於插入排序的排序算法.
希爾排序爲了加快速度, 簡單地改進了插入排序. (交換不相鄰的元素以對數組的局部進行排序, 並最終用插入排序將局部有序的數組排序.
思想: 使數組中任意間隔 h 的元素都是有序的. 這樣的數組被稱爲 h 有序數組. (一個 h 有序數組就是 h 個互相獨立的有序數組編織在一起組成的一個數組.)
L E E A M H L E P S O L T S X
L ----- M ----- P ----- T
E ----- H ----- S ----- S
E ----- L ----- O ----- X
2.3.2 實現
public class Shell {
public static void sort(Comparable[] a) {
int N = a.length;
int h = 1;
while (h<N/3) { h = 3*h+1; } //?
while (h>=1) {
for (int i=h; i<N; i++) {
for (int j=i; j>=h && less(a[j], a[j-h]); j-=h) {
exch(a, j, j-h);
}
}
h/=3;
}
}
}
2.3.3 代價分析
略
3 習題
3.1 題目1
寫下選擇排序是如何將數組 E A S Y Q U E S T I O N 排序的.
i | Array |
---|---|
0 | A E S Y Q U E S T I O N |
1 | A E S Y Q U E S T I O N |
2 | A E E S Y Q U S T I O N |
3 | A E E I S Y Q U S T O N |
4 | A E E I N S Y Q U S T O |
5 | A E E I N S Y Q U S T O |
6 | A E E I N S S Y Q U T O |
7 | A E E I N S S T Y Q U O |
8 | A E E I N S S T O Y Q U |
9 | A E E I N S S T O Q Y U |
10 | A E E I N S S T O Q U Y |
3.2 題目2
寫下插入排序是如何將數組 E A S Y Q U E S T I O N 排序的.
i | Array |
---|---|
0 | E A S Y Q U E S T I O N |
1 | A E S Y Q U E S T I O N |
2 | A E S Y Q U E S T I O N |
3 | A E S Y Q U E S T I O N |
4 | A E Q S Y U E S T I O N |
5 | A E Q S U Y E S T I O N |
6 | A E E Q S U Y S T I O N |
7 | A E E Q S S U Y T I O N |
8 | A E E Q S S T U Y I O N |
9 | A E E I Q S S T U Y O N |
10 | A E E I O Q S S T U Y N |
11 | A E E I N O Q S S T U Y |