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">