1. 排序的幾個概念
(1)內部排序與外部排序:
整個過程都在內存中進行,則叫內部排序;否則爲外部排序。
(2)主關鍵字和次關鍵字:
若排序過程中主關鍵字相同,則使用次關鍵字進行比較。
(3)排序的穩定性:
若排序前Ki = Kj (i < j),排序後仍然如此,則穩定;否則不穩定。
2. 算法實例
2.1 直接插入排序:在已排好序的集合上繼續插入一個元素構成新的集合。
部分代碼(完整代碼直接插入.c):
void insert_sort (RecordType *r, int l)
{
/* 數組元素 r[0]是監視哨 */
int i, j;
for (i = 2; i <= l; i++) {
r[0] = r[i];
/* 從後向前比較,邊比較邊移動 */
for (j = i-1; r[0].key < r[j].key; j--)
r[j+1] = r[j];
r[j+1] = r[0];
}
}
時空複雜度:T(n) = O(n2), S(n) = O(1)
2.2 折半插入排序:利用折半的思想在 {1...i-1} 中查找 i 的位置
部分代碼(完整代碼折半插入.c):
void insert_sort (RecordType *r, int l)
{
/* 數組元素 r[0]是監視哨 */
int i, j;
int low, high, mid;
for (i = 2; i <= l; i++) {
r[0] = r[i];
low = 1, high = i - 1;
while (low <= high) {
mid = (low + high)/2;
if (r[0].key < r[mid].key)
high = mid-1;
else
low = mid + 1;
}
for (j = i-1; j >= low; j--)
r[j+1] = r[j];
r[low] = r[0];
}
}
時空複雜度:T(n) = O(n2), S(n) = O(1)2.3 shell排序:分組,組內進行直接插入排序的思想,多次重複減小組內間距
部分代碼(完整代碼shell排序.c)
void shell_insert (int r[], int l, int delta)
{
int i, j;
for (i = 1+delta; i <= l; i++) {
if (r[i] < r[i-delta]) {
r[0] = r[i];
// 組內進行直接插入排序
for (j = i-delta; j > 0 && r[0] < r[j]; j-=delta)
r[j+delta] = r[j];
r[j+delta] = r[0];
}
}
}
void shell_sort (int r[], int l, int delta[], int n)
{
int i;
for (i = 0; i < n; i++) {
shell_insert (r, l, delta[i]);
}
}
時間複雜度:T(n) = O(n1.5)
2.4 冒泡排序:第 i 趟對前前 n-i 個元素進行相鄰節點比較,若在某一趟中沒有發現一個逆序,則可以終止比較
部分代碼(完整代碼bubble.c)
#define SWAP(x, y, type) \
{type __swap_; \
__swap_ = x; \
x = y; \
y = __swap_;}
void bubble_sort (int r[], int l)
{
int i, j;
int t;
unsigned char change = TRUE;
for (i = 0; i < l && change; i++) {
change = FALSE;
for (j = 0; j < l-i-1; j++)
if (r[j] > r[j+1]) {
SWAP (r[j], r[j+1], int);
change = TRUE;
}
}
}
2.5 快速排序:快排是對冒泡的一種改進,將軸從到輔助單元中,以軸爲中心從左右兩邊開始比較,如右邊碰到比軸小的則放到low裏邊,若左邊碰到比軸大的則放到hegh裏邊直到low >= high, 一趟結束,如此重複左邊和右邊的記錄
部分代碼(完整代碼QKSort.c)
void QK_sort (int r[], int low, int high)
{
if (low < high) {
int pos = QK_pass (r, low, high);
QK_sort (r, low, pos-1);
QK_sort (r, pos+1, high);
}
}
int QK_pass (int r[], int low, int high)
{
int x = r[low];
while (low < high) {
// 從右向左找小於x的元素
while (low < high && r[high] >= x)
high--;
// 找到小的送入low單元
if (low < high)
r[low++] = r[high];
// 從左向右找大於等於x的元素
while (low < high && r[low] < x)
low++;
// 找到大的送入high單元
if (low < high)
r[high--] = r[low];
}
// 插入基準到 low = high單元
r[low] = x;
// 返回基準位置
return low;
}
2.6 選擇排序:在i+1到n這段記錄中找到小於i的且最小的和i進行交換,完成一趟,如此重複
部分代碼(完整代碼selectSort.c)
void select_sort (int r[], int n)
{
int i, j;
for (i = 0; i < n; i++) {
int k = i;
for (j = i+1; j < n; j++) {
// 記錄k的位置,k是j到n-1小於i且最小的
if (r[j] < r[k])
k = j;
// 如果有這樣的k則替換
if (k != i)
SWAP (r[i], r[k], int);
}
}
}
2.6 堆排序排序:sift函數創建大根堆,然後在排序時將根和堆尾交換,堆尾移出,重建大根堆重複
部分代碼(完整代碼heapSort.c)
void sift (int r[], int k, int m)
{
int t = r[k];
int i = k;
int j = 2*i;
unsigned char finished = FALSE;
while (j <= m && !finished) {
// 找到i的孩子中最大的孩子
if (j < m && r[j] < r[j+1])
j++;
// 根比孩子都大,則結束
if (t >= r[j])
finished = TRUE;
else {
// 否則上移,繼續比較
r[i] = r[j];
i = j;
j = 2*i;
}
}
r[i] = t;
}
void crt_heap (int r[], int l)
{
int i;
for (i = l/2; i >= 1; i--)
// 自第Ln/2」個記錄開始重建堆
sift (r, i, l);
}
void heap_sort (int r[], int l)
{
crt_heap (r, l);
int i;
for (i = l; i >= 2; i--) {
// 存放堆尾
int b = r[1];
// 堆尾放到堆首
r[1] = r[i];
// 將b,將根存放到堆尾
r[i] = b;
// 重建堆
sift (r, 1, i-1);
}
}
</pre><pre code_snippet_id="254386" snippet_file_name="blog_20140325_8_8485226" name="code" class="plain">