CSDN话题挑战赛第2期
参赛话题:学习笔记
前言
本文主要针对直接插入排序、希尔排序、快速排序、堆排序、归并排序等九个排序算法的关键代码进行汇总,方便复习。
各个排序算法的基本概念及相关习题等知识点,可见下方链接:
【考研】数据结构考点——直接插入排序_住在阳光的心里的博客-CSDN博客
【考研】数据结构考点——折半查找和折半插入排序_住在阳光的心里的博客-CSDN博客
【考研】数据结构考点——希尔排序_住在阳光的心里的博客-CSDN博客_希尔排序dk
【考研】数据结构考点——直接选择排序_住在阳光的心里的博客-CSDN博客
【考研】数据结构考点——堆排序(含408真题)_住在阳光的心里的博客-CSDN博客
【考研】数据结构考点——冒泡排序(含408真题)_住在阳光的心里的博客-CSDN博客
【考研】数据结构考点——快速排序(重点,含408真题)_住在阳光的心里的博客-CSDN博客
【考研】数据结构考点——归并排序_住在阳光的心里的博客-CSDN博客
【考研】数据结构考点——链式基数排序_住在阳光的心里的博客-CSDN博客
排序算法中,也是考研常考的应用题型,可参考下方的第一个链接,题目讲解详细,方便复习。
可搭配以下链接一起学习:
【2023考研】数据结构常考应用典型例题(含真题)_住在阳光的心里的博客-CSDN博客
【考研】《数据结构》知识点总结.pdf_其它文档类资源-CSDN文库
目录
一、常考的排序算法代码
//以下所有排序算法,若使用顺序存储结构,基本使用以下定义:
//数据元素类型定义
#define MAXSIZE 20 //顺序表的最大长度
typedef int KeyType; //定义关键字类型为整型
typedef struct{
KeyType key; //关键字项
InfoType otherinfo; //其他数据项
}RedType; //记录类型
typedef struct {
RedType r[MAXSIZE + 1]; // r[0]闲置或用做哨兵单元
int length; //顺序表长度
}SqList; //顺序表类型
1、折半排序
//第一种
//对顺序表 L 做折半插入排序
void BInsertSort (SqList &L){
for(i=2; i <= L.length; ++i){
L.r[0] = L.r[1]; //将待插入的记录暂存到监视哨中
//置查找区间初值
low = 1;
high = i-1;
//在r[low..high]中折半查找插入的位置
while (low <= high){
m = (low + high)/2; //折半
if(L.r[0].key < L.r[m].key) high = m - 1; //插入点在前一子表
else low = m + 1; //插入点在后一子表
}
for (j = i-1; j >= high+1; --j)
L.r[j+1] = L.r[j]; //记录后移
L.r[high+1] = L.r[0]; //将 r[0] 即原 r[i],插入到正确位置
}
}
//第二种
//对顺序表 L 做折半插入排序
void BInsertSort(int a[], int n) { //n = a.size()
int i, j, temp, mid, low, high;
for (i = 1; i < n; ++i) {
temp = a[i]; //将待插入的记录暂存到temp中
//置查找区间初值
low = 0;
high = i - 1;
//在a[low..high]中折半查找插入的位置
while (low <= high) {
mid = (low + high) / 2; //折半
if (a[mid] > temp) high = mid - 1; //插入点在前一子表
else low = mid + 1; //插入点在后一子表
}
for (j = i - 1; j >= high + 1; --j){
a[j + 1] = a[j]; //记录后移
}
a[j + 1] = temp;
}
}
2、快速排序
// 对顺序表 L 中的子表 r[low..high] 进行一趟排序,返回枢轴位置
int Partition(SqList &L, int low, int high){
L.r[0] = L.r[low]; //用子表的第一个记录做枢轴记录
pivotkey = L.r[low].key; //枢轴记录关键字保存在 pivotkey 中
while (low < high){ //从表的两端交替地向中间扫描
while (low < high && L.r[high].key >= pivotkey) --high;
L.r[low] = L.r[high]; //将比枢轴记录小的记录移到低端
while (low < high && L.r[low].key <= pivotkey) ++low;
L.r[high] = L.r[low]; //将比枢轴记录大的记录移到高端
}
L.r[low] = L.r[0]; //枢轴记录到位
return low; //返回枢轴位置
}
//调用前置初值: low = 1; high = L.length;
//对顺序表 L 中的子序列 L.r[low..high] 做快速排序
void QSort (SqList &L, int low, int high){
if (low < high) { //长度大于1
pivotloc = Partition(L, low, high); //将L.r[low..high]一分为二,pivotloc是枢轴位置
QSort(L, low, pivotloc - 1); //对左子表递归排序
QSort(L, pivotloc + 1, high); //对右子表递归排序
}
}
//对顺序表 L 做快速排序
void QuickSort(SqList &L){
QSort(L, 1, L.length);
}
3、归并排序
// 将有序表 R[low..mid] 和 R[mid+1..high] 归并为有序表 T[low..high]
void Merge (RedType R[],RedType &T[], int low, int mid, int high){
i = low;
j = mid + 1;
k = low;
while(i <= mid && j<= high){ //将 R 中记录由小到大地并入 T 中
if(R[i].key <= R[j].key) T[k++] = R[i++];
else T[k++] = R[j++];
}
//只有一个 while 循环会被执行
while(i <= mid) T[k++] = R[i++]; //将剩余的 R[low..mid] 复制到 T 中
while(j <= high) T[k++] = R[j++]; //将剩余的 R[j..high] 复制到 T 中
}
// R[low..high] 归并排序后放入 T[low..high] 中
void MSort (RedType R[], RedType &T[], int low, int high){
if (1ow == high) T[low] = R[low];
else{
mid = (low + high) / 2; //将当前序列一分为二,求出分裂点 mid
//对子序列 R[low..mid] 递归归并排序,结果放入 S[low..mid]
MSort (R, S, low, mid);
//对子序列 R[mid+1..high] 递归归并排序,结果放入 S[mid+1..high]
MSort (R, S, mid+1,high) ;
//将 S[low..mid] 和 S[mid+1..high] 归并到 T[low..high]
Merge (S, T, low, mid, high) ;
}
}
//对顺序表L做归并排序
void MergeSort (SqList &L){
MSort(L.r, L.r, 1, L.length);
}
4、堆排序
//假设r[s+1..m]已经是堆,将r[s..m]调整为以 r[s] 为根的大根堆
void HeapAdjust (SqList &L, int s, int m){
rc = L.r[s] ;
for(j = 2*s; j <= m; j *= 2) //沿 key 较大的孩子结点向下筛选
if (j < m && L.r[j].key < L.r[j+1].key)
++j; // j 为 key 较大的记录的下标
if(rc.key >= L.r[j].key)
break; //rc应插入在位置s上
L.r[s] = L.r[j];
s = j;
}
L.r[s] = rc; //插入
}
//把无序序列 L.r[1..n] 建成大根堆
void CreatHeap (SqList &L){
n = L.length;
for(i = n/2; i > 0; --i) //反复调用HeapAdjust
HeapAdjust (L, i, n);
}
//对顺序表L进行堆排序
void HeapSort (SqList &L){
CreatHeap(L); //把无序序列 L.r[1..L.length] 建成大根堆
for(i = L.length; i > 1; --i){
x = L.r[1]: //将堆顶记录和当前未经排序子序列 L.r[1..i] 中最后一个记录互换
L.r[1] = L.r[i];
L.r[i] = x;
HeapAdjust(L, 1, i-1); //将 L.r[1..i-1] 重新调整为大根堆
}
}
二、其他排序算法代码
5、直接插入排序
//第一种写法
// 对顺序表 L 做直接插入排序
// 从后往前找
void InsertSort (SqList &L){
for(i = 2; i <= L. length; ++i){
if(L.r[i].key < L.r[i-1].key){ //“<", 需将 r[i] 插入有序子表
L.r[0] = L.r[i]; //将待插入的记录暂存到监视哨中
L.r[i] = L.r[i-1]; //r[i-1] 后移
for(j = i-2; L.r[0].key < L.r[j].key; --j) //从后向前寻找插入位置
L.r[j+1] = L.r[j]; //记录逐个后移,直到找到插入位置
L.r[j+1] = L.r[0]; //将 r[0] 即原 r[i], 插入到正确位置
}
}
}
//第二种写法:
//直接插入排序
void InsertSort(int a[], int n) { // n = a.size();
int i, j, temp;
for (i = 1; i < n; i++) { //a[0]为枢轴
if (a[i] < a[i - 1]) {
temp = a[i];
for (j = i; a[j - 1] > temp && j >= 1; j--) {
a[j] = a[j - 1];
}
a[j] = temp;
}
}
}
6、希尔排序
//对顺序表L做一趟增量是dk的希尔插入排序
void ShellInsert (SqList &L,int dk){
for (int i = dk + 1; i <= L.length; ++i){
if(L.r[i].key < L.r[i-dk].key){ //需将 L.r[i] 插入有序增量子表
L.r[0] = L.r[i]; //暂存在L.r[0]
for(j = i - dk; j >0 && L.r[0].key < L.r[j].key; j -= dk){
L.r[j+dk] = L.r[j]; //记录后移,直到找到插入位置
}
L.r[j+dk] = L.r[0]; //将 r[0] 即原 r[i] ,插入到正确位置
}
}
}
//按增量序列 dt[0..t-1] 对顺序表 L 作趟希尔排序
void Shellsort(SqList &L, int dt[], int t){
for(int k = 0; k < t; ++k)
ShellInsert(L, dt[k]); //一趟增量为 dt[t] 的希尔插入排序
}
7、直接选择排序(简单选择排序)
//对顺序表 L 做简单选择排序
void SelectSort (SqList &L)
{
for(i = 1; i < L.length; ++i){ //在L.r[i..L.length]中选择关键字最小的记录
k = i;
for (j = i+1; j <= L.length; ++j){
if(L.r[j].key < L.r[k].key)
k = j; //k指向此趟排序中关键字最小的记录
}
if (k != i){ //交换r[i]与r[k]
t = L.r[i];
L.r[i] = L.r[k];
L.r[k] = t;
}
}
}
8、冒泡排序
//对顺序表 L 做冒泡排序
vold BubbleSort (SqList &L){
// flag 用来标记某趟排序是否发生交换
m = L.length - 1;
flag = 1;
while((m > 0) && (flag == 1)){
flag = 0; //flag置为0,如果本趟排序没有发生交换,则不会执行下趟摔序
for (j = 1; j <= m; j++){
if(L.r[j].key > L.r[j+1].key){
flag = 1; //flag 置为 1, 表示本趟排序发生了交换
//交换前后两个记录
t = L.r[j];
L.r[j] = L.r[j+1];
L.r[j+1] = t;
}
}
--m;
}
}
9、基数排序
#define MAXNUM_ KEY 8 //关键字项数的最大值
#define RADIX 10 //关键字基数,此时是十进制整数的基数
#define MAX_ SPACE 10000
typedef struct{
KeyType keys[MAXNUM_KEY]; //关键字
InfoType otheritems; //其他数据项
int next;
}SLCell; //静态链表的结点类型
typedef struct{
SLCell r[MAX_SPACE]; //静态链表的可利用空间,r[0]为头结点
int keynum; //记录的当前关键字个数
int recnum; //静态链表的当前长度
}SLList; //静态链表类型
typedef int ArrType[RADIX] //数组类型
//静态链表 L 的 r 域中记录已按(keys[0], ..., keys[i-1]有序
//本算法按第 i 个关键字 keys[i] 建立 RADIX 个子表,使同子表中记录的 keys[i] 相同
//f[0..RADIX-1] 和 e[0..RADTX-1] 分别指向各子表中第一个和最后一个记录
void Distribute(SLcell &r, int i, ArrType &f, ArrType &e){
for(j = 0; j < RADIX; ++j) f[j] = 0; //各子表初始化为空表
for(p = r[0].next; p; p = r[p].next){
j = ord(r[p].keys[i]); //将记录中第 i 个关键字映射到 [0..RADIX-1]
if(!f[j]) f[j] = p;
else r[e[j]].next = p;
e[j] = p; //将P所指的结点插入第 j 个子表中
}
}
// 本算法按 keys[i] 自小至大地将 f[0..RADIX-1] 所指各子表依次链接成一个链表
// e[0..RADIX-1] 为各子表的尾指针
void Collect (SLCell &r, int i, ArrType f, ArrType e){
for (j = 0; !f[j]; j = succ(j)); //找第一个非空子表,succ 为求后继函数
r[0].next = f[j];
t = e[j]; //r[0].next 指向第一个非空子表中第一个结点
while (j < RADIX){
for(j = succ(j); j < RADIX-1 && !f[j]; j = succ(j)); //找下一个非空子表
if(f[j]){ //链接两个非空子表
r[t].next = f[j];
t = e[j];
}
}
r[t].next = 0; // t 指向最后一个非空子表中的最后一个结点
}
// L 是采用静态链表表示的顺序表
// 对 L 做基数排序,使得 L 成为按关键字自小到大的有序静态链表,L.r[0]为头结点
void Radixsort(SLList &L){
for(i = O; i < L.recnum; ++i) L.r[i].next = i + 1;
L.r[L.recnum].next = 0; //将 L 改造为静态链表
for(i = O; i < L.keynum; ++i){ //按最低位优先依次对各关键字进行分配和收集
Distribute(L.r, i, f, e); //第i趟分配
Collect(L.r, i, f, e); //第i趟收集
}
}