排序
- 冒泡排序
- 初级冒泡排序:
- 冒泡排序:
- 冒泡排序(优化):
- 选择排序:
- 直接插入排序:
- 希尔排序:
- 堆排序:
- 归并排序
- 归并排序(递归):
- 归并排序(非递归):
- 快速排序:
- 快速排序:
- 快速排序(优化):
Program ended with exit code: 0
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
//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]);
printf("\n");
}
1、冒泡排序
- 初级冒泡排序:
- 冒泡排序:
- 冒泡排序(优化):
//4. 冒泡排序-对顺序表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);
}
}
}
//5.冒泡排序-对顺序表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);
}
}
}
//6.冒泡排序-对顺序表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、选择排序:
//7.选择排序--对顺序表L进行简单选择排序
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、直接插入排序:
//8.直接插入排序算法--对顺序表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、希尔排序:
//9.希尔排序-对顺序表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、堆排序:
//大顶堆调整函数;
/*
条件: 在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);
}
}
6、归并排序
- 归并排序(递归):
- 归并排序(非递归):
//11.归并排序-对顺序表L进行归并排序
//③ 将有序的SR[i..mid]和SR[mid+1..n]归并为有序的TR[i..n]
void Merge(int SR[],int TR[],int i,int m,int n)
{
int j,k,l;
//1.将SR中记录由小到大地并入TR
for(j=m+1,k=i;i<=m && j<=n;k++)
{
if (SR[i]<SR[j])
TR[k]=SR[i++];
else
TR[k]=SR[j++];
}
//2.将剩余的SR[i..mid]复制到TR
if(i<=m)
{
for(l=0;l<=m-i;l++)
TR[k+l]=SR[i+l];
}
//3.将剩余的SR[j..mid]复制到TR
if(j<=n)
{
for(l=0;l<=n-j;l++)
TR[k+l]=SR[j+l];
}
}
//② 将SR[s...t] 归并排序为 TR1[s...t];
void MSort(int SR[],int TR1[],int low, int hight){
int mid;
int TR2[MAXSIZE+1];
if(low == hight)
TR1[low] = SR[low];
else{
//1.将SR[low...hight] 平分成 SR[low...mid] 和 SR[mid+1,hight];
mid = (low + hight)/2;
//2. 递归将SR[low,mid]归并为有序的TR2[low,mid];
MSort(SR, TR2, low, mid);
//3. 递归将SR[mid+1,hight]归并为有序的TR2[mid+1,hight];
MSort(SR, TR2, mid+1, hight);
//4. 将TR2[low,mid] 与 TR2[mid+1,hight], 归并到TR1[low,hight]中
Merge(TR2, TR1, low, mid, hight);
}
}
//① 对顺序表L进行归并排序
void MergeSort(SqList *L){
MSort(L->r,L->r,1,L->length);
}
//12.归并排序(非递归)-->对顺序表L进行非递归排序
//对SR数组中相邻长度为s的子序列进行两两归并到TR[]数组中;
void MergePass(int SR[],int TR[],int s,int length){
int i = 1;
int j;
//①合并数组
//s=1 循环结束位置:8 (9-2*1+1=8)
//s=2 循环结束位置:6 (9-2*2+1=6)
//s=4 循环结束位置:2 (9-2*4+1=2)
//s=8 循环结束位置:-6(9-2*8+1=-6) s = 8时,不会进入到循环;
while (i<= length-2*s+1) {
//两两归并(合并相邻的2段数据)
Merge(SR, TR, i, i+s-1, i+2*s-1);
i = i+2*s;
/*
s = 1,i = 1,Merge(SR,TR,1,1,2);
s = 1,i = 3,Merge(SR,TR,3,3,4);
s = 1,i = 5,Merge(SR,TR,5,5,6);
s = 1,i = 7,Merge(SR,TR,7,7,8);
s = 1,i = 9,退出循环;
*/
/*
s = 2,i = 1,Merge(SR,TR,1,2,4);
s = 2,i = 5,Merge(SR,TR,5,6,8);
s = 2,i = 9,退出循环;
*/
/*
s = 4,i = 1,Merge(SR,TR,1,4,8);
s = 4,i = 9,退出循环;
*/
}
//②如果i<length-s+1,表示有2个长度不等的子序列. 其中一个长度为length,另一个小于length
// 1 < (9-8+1)(2)
//s = 8时, 1 < (9-8+1)
if(i < length-s+1){
//Merge(SR,TR,1,8,9)
Merge(SR, TR, i, i+s-1, length);
}else{
//③只剩下一个子序列;
for (j = i; j <=length; j++) {
TR[j] = SR[j];
}
}
}
void MergeSort2(SqList *L){
int *TR = (int *)malloc(sizeof(int) * L->length);
int k = 1;
//k的拆分变换是 1,2,4,8;
while (k < L->length) {
//将SR数组按照s=2的长度进行拆分合并,结果存储到TR数组中;
//注意:此时经过第一轮的归并排序的结果是存储到TR数组了;
MergePass(L->r, TR, k, L->length);
k = 2*k;
//将刚刚归并排序后的TR数组,按照s = 2k的长度进行拆分合并. 结果存储到L->r数组中;
//注意:因为上一轮的排序的结果是存储到TR数组,所以这次排序的数据应该是再次对TR数组排序;
MergePass(TR, L->r, k, L->length);
k = 2*k;
}
}
7、快速排序:
- 快速排序:
- 快速排序(优化):
//13.快速排序-对顺序表L进行快速排序
//③交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置
//此时在它之前(后)的记录均不大(小)于它
int Partition(SqList *L,int low,int high){
int pivotkey;
//pivokey 保存子表中第1个记录作为枢轴记录;
pivotkey = L->r[low];
//① 从表的两端交替地向中间扫描;
while (low < high) {
//② 比较,从高位开始,找到比pivokey更小的值的下标位置;
while (low < high && L->r[high] >= pivotkey)
high--;
//③ 将比枢轴值小的记录交换到低端;
swap(L, low, high);
//④ 比较,从低位开始,找到比pivokey更大的值的下标位置;
while (low < high && L->r[low] <= pivotkey)
low++;
//⑤ 将比枢轴值大的记录交换到高端;
swap(L, low, high);
}
//返回枢轴pivokey 所在位置;
return low;
}
//② 对顺序表L的子序列L->r[low,high]做快速排序;
void QSort(SqList *L,int low,int high){
int pivot;
if(low < high){
//将L->r[low,high]一分为二,算出中枢轴值 pivot;
pivot = Partition(L, low, high);
printf("pivot = %d L->r[%d] = %d\n",pivot,pivot,L->r[pivot]);
//对低子表递归排序;
QSort(L, low, pivot-1);
//对高子表递归排序
QSort(L, pivot+1, high);
}
}
//① 调用快速排序(为了保证一致的调用风格)
void QucikSort(SqList *L){
QSort(L, 1, L->length);
}
//14 快速排序-优化
int Partition2(SqList *L,int low,int high){
int pivotkey;
/**1.优化选择枢轴**/
//① 计算数组中间的元素的下标值;
int m = low + (high - low)/2;
//② 将数组中的L->r[low] 是整个序列中左中右3个关键字的中间值;
//交换左端与右端的数据,保证左端较小;[9,1,5,8,3,7,4,6,2]
if(L->r[low]>L->r[high])
swap(L, low, high);
//交换中间与右端的数据,保证中间较小; [2,1,5,8,3,7,4,6,9];
if(L->r[m]>L->r[high])
swap(L, high, m);
//交换中间与左端,保证左端较小;[2,1,5,8,3,7,4,6,9]
if(L->r[m]>L->r[low])
swap(L, m, low);
//交换后的序列:3,1,5,8,2,7,4,6,9
//此时low = 3; 那么此时一定比选择 9,2更合适;
/**2.优化不必要的交换**/
//pivokey 保存子表中第1个记录作为枢轴记录;
pivotkey = L->r[low];
//将枢轴关键字备份到L->r[0];
L->r[0] = pivotkey;
//① 从表的两端交替地向中间扫描;
while (low < high) {
//② 比较,从高位开始,找到比pivokey更小的值的下标位置;
while (low < high && L->r[high] >= pivotkey)
high--;
//③ 将比枢轴值小的记录交换到低端;
//swap(L, low, high);
//③ 采用替换的方式将比枢轴值小的记录替换到低端
L->r[low] = L->r[high];
//④ 比较,从低位开始,找到比pivokey更大的值的下标位置;
while (low < high && L->r[low] <= pivotkey)
low++;
//⑤ 将比枢轴值大的记录交换到高端;
//swap(L, low, high);
//⑤ 采样替换的方式将比枢轴值大的记录替换到高端
L->r[high] = L->r[low];
}
//将枢轴数值替换会L->r[low]
L->r[low] = L->r[0];
//返回枢轴pivokey 所在位置;
return low;
}
//② 对顺序表L的子序列L->r[low,high]做快速排序;
#define MAX_LENGTH_INSERT_SORT 7 //数组长度的阀值
void QSort2(SqList *L,int low,int high){
int pivot;
//if(low < high){
//当high-low 大于常数阀值是用快速排序;
if((high-low)>MAX_LENGTH_INSERT_SORT){
//将L->r[low,high]一分为二,算出中枢轴值 pivot;
pivot = Partition(L, low, high);
printf("pivot = %d L->r[%d] = %d\n",pivot,pivot,L->r[pivot]);
//对低子表递归排序;
QSort(L, low, pivot-1);
//对高子表递归排序
QSort(L, pivot+1, high);
}else{
//当high-low小于常数阀值是用直接插入排序
InsertSort(L);
}
}
//① 快速排序优化
void QuickSort2(SqList *L)
{
QSort2(L,1,L->length);
}
验证
int main(int argc, const char * argv[]) {
// insert code here...
printf("Hello, 排序算法\n");
int i;
int d[N]={-7,1,5,8,3,7,4,6,2};
//int d[N]={9,8,7,6,5,4,3,2,1};å
//int d[N]={50,10,90,30,70,40,80,60,20};
SqList l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10;
for(i=0;i<N;i++)
l0.r[i+1]=d[i];
l0.length=N;
l1=l2=l3=l4=l5=l6=l7=l8=l9=l10=l0;
printf("排序前:\n");
print(l0);
printf("\n");
//1.初级冒泡排序
printf("初级冒泡排序:\n");
BubbleSort0(&l0);
print(l0);
printf("\n");
//2.冒泡排序
printf("冒泡排序:\n");
BubbleSort(&l1);
print(l1);
printf("\n");
//3.冒泡排序优化
printf("冒泡排序(优化):\n");
BubbleSort2(&l2);
print(l2);
printf("\n");
//4.选择排序
printf("选择排序:\n");
SelectSort(&l3);
print(l3);
printf("\n");
//5.直接插入排序
printf("直接插入排序:\n");
InsertSort(&l4);
print(l4);
printf("\n");
//6.希尔排序
printf("希尔排序:\n");
shellSort(&l5);
print(l5);
printf("\n");
//7.堆排序
//注意:执行对排序时更换一下数据源. 这里我为什么要这组数据,原因是为了与下标个位数字讲解时进行区分;因为在这个算法讲解过程,出现了很多下标的相关计算.
/* int d[N]={50,10,90,30,70,40,80,60,20}; */
printf("堆排序:\n");
HeapSort(&l6);
print(l6);
printf("\n");
//8.归并排序(递归)
printf("归并排序(递归):\n");
MergeSort(&l7);
print(l7);
printf("\n");
//9.归并排序(非递归)
printf("归并排序(非递归):\n");
MergeSort2(&l8);
print(l8);
printf("\n");
//10.快速排序
printf("快速排序:\n");
QucikSort(&l9);
print(l9);
printf("\n");
//11.快速排序-优化
printf("快速排序(优化):\n");
QuickSort2(&l10);
print(l10);
printf("\n");
printf("\n");
return 0;
}
结果
Hello, 排序算法
排序前:
-7,1,5,8,3,7,4,6,2
初级冒泡排序:
-7,1,2,3,4,5,6,7,8
冒泡排序:
-7,1,2,3,4,5,6,7,8
冒泡排序(优化):
-7,1,2,3,4,5,6,7,8
选择排序:
-7,1,2,3,4,5,6,7,8
直接插入排序:
-7,1,2,3,4,5,6,7,8
希尔排序:
-7,1,2,3,4,5,6,7,8
堆排序:
-7,1,2,3,4,5,6,7,8
归并排序(递归):
-7,1,2,3,4,5,6,7,8
归并排序(非递归):
-7,1,2,3,4,5,6,7,8
快速排序:
pivot = 1 L->r[1] = -7
pivot = 2 L->r[2] = 1
pivot = 6 L->r[6] = 5
pivot = 3 L->r[3] = 2
pivot = 5 L->r[5] = 4
pivot = 8 L->r[8] = 7
-7,1,2,3,4,5,6,7,8
快速排序(优化):
pivot = 1 L->r[1] = -7
pivot = 2 L->r[2] = 1
pivot = 6 L->r[6] = 5
pivot = 3 L->r[3] = 2
pivot = 5 L->r[5] = 4
pivot = 8 L->r[8] = 7
-7,1,2,3,4,5,6,7,8
Program ended with exit code: 0
6、