详细可以查看:https://www.cnblogs.com/onepixel/p/7674659.html
一、概念
1.排序:假设含有n个记录的序列列为(r1,r2,.....,rn). 其相应的关键字分别为{k1,k2,......,kn}. 需确定 1,2,......,n 的一种排序p1,p2,......pn. 使其相应的关键字满足kp1 <= kp2 <= ...... <= kpn 非递减(或 非递增)关系. 即使得到序列成为一个按关键字有序的序列(rp1,rp2,...,rpn);
2.排序的分类:
- 内排序:是在排序整个过程中,待排序的所有记录全部被放置在内存中;
- 外排序:由于排序的记录个数太多,不能同时放置在内存,整个排序过程需要在内外存 之间多次交换数据才能进⾏;
3.排序的结构设计与交换函数实现:
1.排序算法数据结构设计
//⽤用于要排序数组个数最⼤大值,可根据需要修改
#define MAXSIZE 10000
typedef struct {
//⽤用于存储要排序数组,r[0]⽤用作哨兵或临时变量
int r[MAXSIZE+1];
//⽤用于记录顺序表的⻓长度
int length;
}SqList
2.排序常⽤用交换函数实现
//交换L中数组r的下标为i和j的值
void swap(SqList *L ,int i ,int j)
{
int temp=L->r[I];
L->r[i]=L->r[j];
L->r[j]=temp;
}
3.数组打印
void print(SqList L) {
int I;
for(i=1;i< L.length ;i++) printf("%d,",L.r[i]);
printf("%d",L.r[i]);
}
二、排序方式
1.冒泡排序 (Bubble Sort)
- 冒泡排序(Bubble Sort) 一种交换排序,它的基本思想就是: 两两比较相邻的记录的关键字,如果 反序则交换,直到没有反序的记录为止.
- 代码如下:
//冒泡排序-对顺序表L进行交换排序(冒泡排序初级版本)
void BubbleSort0(SqList *L){
int i,j;
for (i = 1; i < L->length; i++) {
for (j = i+1; j <= L->length; j++) {
if(L->r[i] > L->r[j])
swap(L, i, j);
}
}
}
//冒泡排序-对顺序表L作冒泡排序(正宗冒泡排序算法)
void BubbleSort(SqList *L){
int i,j;
for (i = 1; i < L->length; i++) {
//注意:j是从后面往前循环
for (j = L->length-1; j>=i; j--) {
//若前者大于后者(注意与上一个算法区别所在)
if(L->r[j]>L->r[j+1])
//交换L->r[j]与L->r[j+1]的值;
swap(L, j, j+1);
}
}
}
//冒泡排序-对顺序表L冒泡排序进行优化
void BubbleSort2(SqList *L){
int i,j;
//flag用作标记
Status flag = TRUE;
//i从[1,L->length) 遍历;
//如果flag为False退出循环. 表示已经出现过一次j从L->Length-1 到 i的过程,都没有交换的状态;
for (i = 1; i < L->length && flag; i++) {
//flag 每次都初始化为FALSE
flag = FALSE;
for (j = L->length-1; j>=i; j--) {
if(L->r[j] > L->r[j+1]){
//交换L->r[j]和L->r[j+1]值;
swap(L, j, j+1);
//如果有任何数据的交换动作,则将flag改为true;
flag=TRUE;
}
}
}
}
2.选择排序
- 简单排序算法(Simple Selection Sort) 就是通过n-i次关键词比较,从n-i+1个记录中找出关键 字最小的记录,并和第i(1<=i<=n) 个记录进行交换.
- 代码如下:
void SelectSort(SqList *L){
int i,j,min;
for (i = 1; i < L->length; i++) {
//① 将当前下标假设为最小值的下标
min = i;
//② 循环比较i之后的所有数据
for (j = i+1; j <= L->length; j++) {
//③ 如果有小于当前最小值的关键字,将此关键字的下标赋值给min
if (L->r[min] > L->r[j]) {
min = j;
}
}
//④ 如果min不等于i,说明找到了最小值,则交换2个位置下的关键字
if(i!=min)
swap(L, i, min);
}
}
3.直接插⼊排序(Straight Insertion Sort)
- 直接插入排序算法(Stright Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表 中,从而得到一个新的,记录数增1的有序表;
- 代码如下:
直接插入排序算法--对顺序表L进行直接插入排序
void InsertSort(SqList *L){
int i,j;
//L->r[0] 哨兵 可以把temp改为L->r[0]
int temp=0;
//假设排序的序列集是{0,5,4,3,6,2};
//i从2开始的意思是我们假设5已经放好了. 后面的牌(4,3,6,2)是插入到它的左侧或者右侧
for(i=2;i<=L->length;i++)
{
//需将L->r[i]插入有序子表
if (L->r[i]<L->r[i-1])
{
//设置哨兵 可以把temp改为L->r[0]
temp = L->r[i];
for(j=i-1;L->r[j]>temp;j--)
//记录后移
L->r[j+1]=L->r[j];
//插入到正确位置 可以把temp改为L->r[0]
L->r[j+1]=temp;
}
}
}
4.希尔排序原理理(Shell Sort)
- 希尔排序思想: 希尔排序是把记录按下标的⼀定增量分组,对每组使用直接插入排序算法排 序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减⾄至1时,整个序列恰被分成 一组,算法便终止.
- 代码如下:
希尔排序-对顺序表L希尔排序
void shellSort(SqList *L){
int i,j;
int increment = L->length;
//0,9,1,5,8,3,7,4,6,2
//① 当increment 为1时,表示希尔排序结束
do{
//② 增量序列
increment = increment/3+1;
//③ i的待插入序列数据 [increment+1 , length]
for (i = increment+1; i <= L->length; i++) {
//④ 如果r[i] 小于它的序列组元素则进行插入排序,例如3和9. 3比9小,所以需要将3与9的位置交换
if (L->r[i] < L->r[i-increment]) {
//⑤ 将需要插入的L->r[i]暂时存储在L->r[0].和插入排序的temp 是一个概念;
L->r[0] = L->r[i];
//⑥ 记录后移
for (j = i-increment; j > 0 && L->r[0]<L->r[j]; j-=increment) {
L->r[j+increment] = L->r[j];
}
//⑦ 将L->r[0]插入到L->r[j+increment]的位置上;
L->r[j+increment] = L->r[0];
}
}
}while (increment > 1);
}
5.堆排序 (Heap Sort) (完全二叉树)
- 大顶堆:每个结点的值都⼤大于或等于其左右孩⼦子结点的值;
- 每个结点的值都⼩小于等于其左右孩⼦子的结点的值;
- 满足条件:{Ki≥K2i Ki ≥ K2i+1}或 {Ki≤K2i 1≤i≤ Ki ≤ K2i+1},1<=I <=n/2;
- 堆排序(Heap Sort) 就是利⽤堆(假设我们选择大顶堆)进行排序的算法.它的基本思 想:
① 将待排序的序列列构成一个大顶堆,此时,整个序列的最大值就堆顶的根结点,将 它移走(其实就是将其与堆数组的末尾元素交换, 此时末尾元素就是最大值);
② 然后将剩余的n-1个序列重新构成⼀个队,这样就会得到n个元素的次大值, 如此重复执行,就能得到⼀个有序序列了;
堆排序的思路:
①将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
② 将堆顶元素与末尾元素交换,将最大元素”沉"到数组末端;
③ 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+ 交换步骤,直到整个序列有序;
- 代码如下:
//大顶堆调整函数;
/*
条件: 在L.r[s...m] 记录中除了下标s对应的关键字L.r[s]不符合大顶堆定义,其他均满足;
结果: 调整L.r[s]的关键字,使得L->r[s...m]这个范围内符合大顶堆定义.
*/
void HeapAjust(SqList *L,int s,int m){
int temp,j;
//① 将L->r[s] 存储到temp ,方便后面的交换过程;
temp = L->r[s];
//② j 为什么从2*s 开始进行循环,以及它的递增条件为什么是j*2
//因为这是颗完全二叉树,而s也是非叶子根结点. 所以它的左孩子一定是2*s,而右孩子则是2s+1;(二叉树性质5)
for (j = 2 * s; j <=m; j*=2) {
//③ 判断j是否是最后一个结点, 并且找到左右孩子中最大的结点;
//如果左孩子小于右孩子,那么j++; 否则不自增1. 因为它本身就比右孩子大;
if(j < m && L->r[j] < L->r[j+1])
++j;
//④ 比较当前的temp 是不是比较左右孩子大; 如果大则表示我们已经构建成大顶堆了;
if(temp >= L->r[j])
break;
//⑤ 将L->[j] 的值赋值给非叶子根结点
L->r[s] = L->r[j];
//⑥ 将s指向j; 因为此时L.r[4] = 60, L.r[8]=60. 那我们需要记录这8的索引信息.等退出循环时,能够把temp值30 覆盖到L.r[8] = 30. 这样才实现了30与60的交换;
s = j;
}
//⑦ 将L->r[s] = temp. 其实就是把L.r[8] = L.r[4] 进行交换;
L->r[s] = temp;
}
//10.堆排序--对顺序表进行堆排序
void HeapSort(SqList *L){
int i;
//1.将现在待排序的序列构建成一个大顶堆;
//将L构建成一个大顶堆;
//i为什么是从length/2.因为在对大顶堆的调整其实是对非叶子的根结点调整.
for(i=L->length/2; i>0;i--){
HeapAjust(L, i, L->length);
}
//2.逐步将每个最大的值根结点与末尾元素进行交换,并且再调整成大顶堆
for(i = L->length; i > 1; i--){
//① 将堆顶记录与当前未经排序子序列的最后一个记录进行交换;
swap(L, 1, i);
//② 将L->r[1...i-1]重新调整成大顶堆;
HeapAjust(L, 1, i-1);
}
}
网友评论