排序算法速查
OVERVIEW
1.插入排序
(1)直接插入排序
//直接插入排序 不带哨兵
void insertSort(SqList &L) {
int i, j;
for (i = 0; i < L.length; ++i) {
if (L.elem[i] < L.elem[i - 1]) {
int x = L.elem[i];
for (j = i - 1; L.elem[j] > x && j >= 0; --j)//顺序查找找到插入的位置
L.elem[j + 1] = L.elem[j];//所有大于x的记录都将后移
L.elem[j + 1] = x;//插入元素
}
}
}
(2)折半插入排序
//折半插入排序 不带哨兵
void BInsertSort1(SqList &L) {
for (int i = 0; i < L.length; ++i) {
int x = L.elem[i];//插入排序目标值
//利用二分查找寻找插入位置 high + 1为插入位置
int low = 0;
int high = i - 1;
while (low <= high) {
int mid = (low + high)/2;
if (x < L.elem[mid]) high = mid - 1;
else low = mid + 1;
}
//查找位置结束后 进行常规插入操作
for (int j = i - 1; j >= high + 1; --j) L.elem[j + 1] = L.elem[j];//j+1插入位置后的元素后移
L.elem[high + 1] = x;//插入元素
}
}
/*
直到low > high时才停止折半查找
当mid所指元素等于当元素时 应继续令low = mid + 1以保证稳定性
最终应将当前元素插入到low所指的位置(high + 1)
*/
(3)希尔排序
//间隔内直接插入排序
void shellInsert(SqList &L, int dk) {
//对顺序表L进行一趟增量为dk的shell排序,其中dk为步长因子
for (int i = dk + 1; i <= L.length; ++i) {
if (r[i].key < r[i - dk].key) {
r[0] = r[i];
for (int j = i - dk; j > 0 && (r[0].key < r[j].key); j = j - dk) r[j + dk] = r[j];
r[j + dk] = r[0];
}
}
}
void shellSort(SqList &L, int dlta[], int t) {
//dk值依次存在dlta[t]中
//按增量序列dlta[0..t-1]对顺序表L作希尔排序
for (int k = 0; k < t; ++k) {
shellInsert(L, dlta[k]);//一趟增量为dlta[k]的插入排序
}
}
(4)表插入排序
//表插入排序
void tableInsertSort(Table &tb, int n) {
//1.将静态链表中数组下标为1的结点和表头结点构成一个循环链表
tb[0].next = 1;
//2.依次将下标为 2~n 的结点按照 关键字非递减 有序插入到循环链表中
int p, q;//q为p的前驱
for (int i = 2; i < n; ++i) {
//调整p、q位置
q = 0;
p = tb[0].next;
while (p != 0 && tb[p].data <= tb[i].data) {
q = p;
p = tb[p].next;
}
//进行插入操作
tb[i].next = tb[q].next;
tb[q].next = i;
}
}
//对表插入排序后的记录进行重排序
//根据静态链表tb中各结点的指针值调整记录位置 使得tb中记录 按关键字非递减
void Arrange(Table &tb,int n) {
int p, q;
p = tb[0].next;
for (int i = 1; i < n; ++i) {
//1.调整p、q位置
while (p < i) p = tb[p].next;//找到第i个记录 并用p指示其在tb中当前的位置
q = tb[p].next;//q指示尚未调整的表尾
//2.进行调整
if (p != i) {
swap(tb[p], tb[i]);//交换整个记录(包括 data和 next) 使第i个记录到位
tb[i].next = p;//tb[i].next指向被移动的记录 使得后续可由 while循环找回
}
//3.p指向尚未调整的表尾 为找第i + 1个记录作准备
p = q;
}
}
2.交换排序
(1)冒泡排序
//i范围为(0 ~ n-2) j范围为(0 ~ n-i-2)
void bubbleSort(SqList &L) {
int n = L.length;
for (int i = 0; i < n - 1; ++i) {//需要n - 1趟
for (int j = 0; j < n - i - 1; ++j) {
if (L.elem[j] > L.elem[j + 1])
swap(L.elem[j], L.elem[j + 1]);//发生逆序则进行交换
}
}
}
//改进的冒泡排序算法:若发现某次遍历后已经是有序的序列,则可直接跳出循环无需再遍历
//新增flag用于标记是否发生交换
void bubbleSort(SqList &L) {
int flag = 1;
int n = L.length;
for (int i = 1; i < n && flag; ++i) {//需要n - 1趟
flag = 0;
for (int j = 0; j < n - i; ++j) {
if (L.elem[j] > L.elem[j + 1]) {
flag = 1;
swap(L.elem[j], L.elem[j + 1]);//发生逆序则进行交换
}
}
}
}
(2)快速排序
//选出pivot并对SqList进行排序
int partition(SqList &L, int low, int high) {
int key = L.elem[low];//取low处元素的值作为比较参考
while(low < high) {
while(low < high && L.elem[high] >= key) --high;//右侧比temp元素大的元素结点不动
L.elem[low] = L.elem[high];//将比key元素小的结点搬到low位置
while(low < high && L.elem[low] <= key) ++low;//左侧比temp元素小的元素结点不动
L.elem[high] = L.elem[low];//将比key元素大的结点搬到high位置
}
L.elem[low] = key;//low = high = pivot
return low;
}
void QSort(SqList &L, int low, int high) {//快速排序调用并指明排序下标范围(low ~ high)
if (low < high) {//排序区间长度大于1则继续递归,否则退出递归
int pivot = partition(L, low, high);//选出pivot并对SqList进行排序
QSort(L, low, pivot - 1);
QSort(L, pivot + 1, high);
}
}
3.选择排序
(1)选择排序
void selectSort(SqList &L) {
for (int i = 0; i < L.length - 1; ++i) {
//从待排序序列中选出最小值
int min = i;
for (int j = i + 1; j < L.length; ++j) {
if (L.elem[j] < L.elem[min]) min = j;//更新最小值位置
}
//如果最小值为自己则不进行元素交换
if (min != i) {
int temp = L.elem[i];
L.elem[i] = L.elem[min];
L.elem[min] = temp;
}
}
}
(2)堆排序
//将以x为根的子树调整为大根堆
void HeapAdjust(int R[], int x, int n) {
int rc = R[x];
for (int i = 2*x; i <= n; i *= 2) {//沿较大的孩子结点向下筛选(2*x即为较大孩子)
if (i < n && R[i] < R[i + 1]) i = i + 1;//对比左右孩子的大小 取key为较大的孩子节点的下标(保证有右孩子)
if (rc >= R[i]) break;//若rc已经满足大根堆的要求 则筛选直接结束
eles {
R[x] = R[i];//将A[i]调整到双亲结点上
x = i;//修改x值为i继续向下筛选(实现树的继续向下筛选)
}
}
R[x] = rc;//被筛选结点的值放入最终位置
}
//堆排序下标范围为 0 - length-1
void HeapSort(int R[], int n) {//对R[1]到R[n]进行堆排序
//1.建立初始堆O(n)
for (int i = (n-1)/2; i >= 0; --i) HeapAdjust(R, i, n);
//2.堆去顶后重构O(nlogn)
for (int i = n - 1; i > 0; --i) {//去顶重构n-1次
swap(R[0], R[i]);//根与最后一个元素交换(去顶)
HeapAdjust(R, 0, i - 1);//重新建堆
}
}
//堆排序下标范围为 1 - length
void HeapSort(int R[], int n) {//对R[1]到R[n]进行堆排序
//1.建立初始堆O(n)
for (int i = n/2; i >= 1; --i) HeapAdjust(R, i, n);
//2.堆去顶后重构O(nlogn)
for (int i = n; i > 1; --i) {//去顶重构n-1次
swap(R[1], R[i]);//根与最后一个元素交换(去顶)
HeapAdjust(R, 1, i - 1);//重新建堆
}
}
4.归并排序
//[low...mid]和A[mid+1...high]各自有序 将两个部分归并
void merge(int A[], int n, int low, int mid, int high) {
int i, j, k;
int *B = (int *)malloc(n*sizeof(int));//辅助数组B
for (k = low; k <= high; ++k) B[k] = A[k];//将A中的所有元素复制到B中
for (i = low, j = mid + 1, k = i; i <= mid && j <= high; ++k) {//归并操作
if (B[i] <= B[j]) A[k] = B[i++];//将较小值复制到A中
else A[k] = B[j++];
}
while(i <= mid) A[k++] = B[i++];//未归并完的部分直接复制到尾部
while(j <= high) A[k++] = B[j++];
}
void mergeSort(int A[], int n, int low, int high) {
if (low < high) {
int mid = (low + high)/2;//从中间划分
mergeSort(A, n, low, mid);//左半部分归并排序
mergeSort(A, n, mid + 1, high);//右半部分归并排序
merge(A, n, low, mid, high);//归并操作
}
}
mergeSort(L.elem, L.length, 0, L.length - 1);
详细使用介绍参见:https://blog.csdn.net/weixin_49167174/article/details/125179719