首先来个插入排序
#include <stdio.h> void swap(int *x, int *y) { int t = *x; *x = *y; *y = t; } void insert_sort(int *x, int length) { int i, j; for (i = 1; i < length; i++) for (j = i; j > 0 && x[j-1] > x[j]; j--) swap(&x[j-1], &x[j]); } int main(void) { int i, a[] = {42, 20, 17, 13, 28, 14, 23, 15}; insert_sort(a, 8); for (i = 0; i < 8;i++) printf("%4d", a[i]); return 0; }
在第二个for循环中,总是给t赋予相同值x[i],可以将其移出第二个for循环
void insert_sort(int *x, int length) { int i, j; for (i = 1; i < length; i++) { int t = x[i]; for (j = i; j > 0 && x[j-1] > t; j--) x[j] = x[j-1]; x[j] = t; } }
一种简单的快速排序
排序数组时,将数组不停分成两个小部分,进行递归排序,分别用l和u表示数组排序部分的下界和上界,递归结束条件是待排序部分的元素个数小于2.
然后围绕某一值t对数组进行划分,重新组织x[a….b],并计算中间元素的下标m,使得所有<t在m左,所有>t在m右
|a <t |m >= t |i ?未处理部分 b|
m = a -1; for i = [a, b] if x[i] < t swap(++m, i);
void q_sort(int l, int u) { if (l >= u) return ;
m = l;
for (int i = l +1; i <= u; i++)
if (x[i] < x[l])
swap(++m, i);
swap(m, l); qsort(l, p -1); qsort(p + 1, u); }
从右侧划分,完整代码
void qsort2(int l, int u) { if (l >= u) return; m = i = u + 1; do { while (x[--i] < x[l]) ; swap(i, --m); } while (i != l) qsort2(l, m - 1); qsort2(m + 1, u); }
当元素相同时,为最坏情况o(n*n),使用双向划分可解决问题,变为o(nlog(n))
主循环中有两个循环,第一个内循环将i向右移过小元素,遇到大元素停止;第二个内循环向左移过大元素,遇到小元素停止。
主循环测试这两个下标若交叉,则交换它们的值。
void qsort3(int l, int u) { if (l >= u) return ; int i = l, j = u + 1, t = x[l]; while (1) { while (x[j] > t) j--; while (i <= u && x[i] < t) i++; if (i > j) break; swap(i, j); } swap(l, j); qsort3(l, j - 1); qsort3(j + 1, u); }
以上都是按照第一个元素进行划分,随机选择划分元素可得到更好的性能 swap(l, randint(l, u));.
int bigrand() { return RAND_MAX*rand() + rand(); } int randint(int l, int u) { return l + bigrand() % (u - l + 1); }
对于任意n元输入数组,快速排序时间都正比于nlogn。
快速排序会花费大量时间来排序很小数组,如果用插入排序累排序小数组,程序速会更快。
void qsort4(int l, int u) { if (u - l < cutoff) return; swap(l. randint(l, u)); int t = x[l], i = l, j = u + 1; for (;;) { while (i <= u && x[i] < t) i++; while (x[j] > t) j--; if (x > j) break; swap(i, j); } swap(j, l); qsort4(l, j - 1); qsrt4(j + 1, u); } void test() { qsort4(0, length - 1) insert_sort(x, length); }