完整代码及文档已上传https://download.csdn.net/download/qq_45772158/12615816
各种内部排序算法的时间复杂度分析结果只给出了算法执行时间的阶,或大概执行时间。试通过随机的数据比较各算法的关键字比较次数和关键字移动次数,以取得直观感受。
基本要求:
(1) 界面友好,易与操作。可采用菜单或其它人机对话方式进行选择。
(2) 实现各种内部排序,包括直接插入排序,冒泡排序,直接选择排序,快速排序,堆排序,归并排序等。
(3) 演示程序以人机对话的形式进行。每次测试完毕显示各种比较指标值的列表,以便比较各种排序的优劣。
测试数据:
利用随机函数产生N个随机整数(10000以上),对这些数进行多种方法进行排序。比较的指标为有关键字参加的比较次数和关键字的移动次数(关键字交换以3次计)。
typedef struct{
KeyType key; //关键字
}RedType;
主函数:
#include
#include
#include
#include
#include
#define MAXSIZE 20000 //排序元素个数
typedef int KeyType; //定义关键字的整数类型
typedef struct{
KeyType key; //关键字
}RedType;
RedType R[MAXSIZE];
typedef struct{
RedType r[MAXSIZE+1]; //0号单元闲置或用作哨兵单元
int length; //顺序表长度
int mov; //记录关键字移动次数
int com; //关键字的比较次数
int dt[];
}Sqlist;
4.1.1 冒泡排序
核心思想
依次比较相邻的两个数,将小数放在前面,大数放在后面,第一轮比较后,最大的数便被放到了最后;第二轮操作前 n-1 个数据(假设有 n 个数据),依然是依次比较相邻的两个数,将小数放在前面,大数放在后面,倒数第二个数便是第二大的数;同理第 i 轮操作前 n-i+1 的数据(假设 i 取值是从 1 开始的),则 n-i+i 位置上的数据为第 i 大的数据。一共有 n-1 轮,第 i 轮比较中共比较 n-i 次比较。
核心代码
void BubbleSort(Sqlist *L) {
int i,j;
int N=L->length;
for(i=0;ilength;i++){
L->r[0]=L->r[1];
for(j=1;j
L->r[j]=L->r[j+1];
L->mov++;}
else{
L->r[j]=L->r[0];
L->r[0]=L->r[j+1];
L->mov+=2;}
L->com++;
L->r[j+1]=L->r[0];}}
printf(“冒泡排序后的排列顺序如下:\n”);
for(i=1;i<=MAXSIZE;i++)
printf("%d\t",L->r[i]);//输出排序后数组和相关数据
printf("\n关键字移动次数为:%d\n",L->mov);
printf(“关键字比较次数为:%d\n”,L->com);
}
4.1.2 直接插入排序
核心思想
经过 i-1 遍处理后 ,L[1…i-1] 己排好序。第 i 遍处理仅将 L[i] 插入 L[1…i-1] 的适当位置,使得 L[1…i] 又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较 L[i] 和 L[i-1] ,如果 L[i-1] ≤ L[i] ,则 L[1…i] 已排好序,第 i 遍处理就结束了;否则交换 L[i] 与 L[i-1] 的位置,继续比较 L[i-1] 和 L[i-2] ,直到找到某一个位置 j(1 ≤ j ≤ i-1) ,使得 L[j] ≤ L[j+1] 时为止
核心代码
void InsertSort(Sqlist *L) {
int i,j;
for(i=2;i<=L->length;i++) {
L->com++;
if((L->r[i].key < L->r[i-1].key)) //如果r[i]的值比r[i-1]的值要小的话
{
L->r[0] = L->r[i]; //将待插入的记录暂存到监视哨中 (为了避免数组下标出界,在r[0]处设置监视哨 )
L->r[i] = L->r[i-1]; //r[i-1]后移
L->mov+=2; //移动次数加2
for(j=i-2;(L->r[0].key < L->r[j].key);j–) //从后向前寻找插入位置
{
L->r[j+1] = L->r[j]; //纪录逐个后移,直到找到插入位置
L->mov++;
L->com++; }
L->r[j+1] = L->r[0]; //将r[0]即原r[i]插入到正确位置
L->mov++;}}
printf(“直接插入排序后的排列顺序如下:\n”);
for(i=1;i<=L->length;i++)
printf("%d\t",L->r[i]);//输出排序后数组和相关数据
printf("\n关键字移动次数为:%d\n",L->mov);
printf(“关键字比较次数为:%d\n”,L->com); }
4.1.3简单选择排序
核心思想
核心思想
首先检查数据列表中的数据数,如果小于两个,则直接退出程序。如果有超过两个以上的数据,就选择一个分割点将数据分成两个部分,小于分割点的数据放在一组,其余的放在另一组,然后分别对两组数据排序。通常分割点的数据是随机选取的。这样无论你的数据是否已被排列过,你所分割成的两个字列表的大小是差不多的。而只要两个子列表的大小差不多
核心代码
int Partition(Sqlist *L,int low,int high)
{ //对顺序表中的子表r[low…high]进行一趟排序,返回枢轴位置
int pivotkey;
L->r[0] = L->r[low]; //用子表的第一个记录做枢轴记录
pivotkey=L->r[low].key; //枢轴纪录关键字
while(low
while(low
{
high–;
L->com++;
}
L->r[low] = L->r[high]; //将比枢轴纪录小的纪录移到低端
L->mov++;
while(low
{
low++;
L->com++;
}
L->r[high] = L->r[low]; //将比枢轴纪录大的纪录移到高端
L->mov++;
}
L->r[low] = L->r[0]; //枢轴记录到位
L->mov++;
return high; //返回枢轴位置
}
void QSort(Sqlist *L, int low, int high) {
int pivotloc,i;
if (low
QSort(L, low, pivotloc-1); // 对左子表递归排序,pivotloc是枢轴位置
QSort(L, pivotloc+1, high); // 对右子表递归排序
}}
void QuickSort(Sqlist *L){ //对顺序表L做快速排序
QSort(L,1,L->length);
printf(“快速排序后的排列顺序如下:\n”);
for(int i=1;i<=L->length;i++)
printf("%d\t",L->r[i]);//输出排序后数组和相关数据
printf("\n关键字移动次数为:%d\n",L->mov);
printf(“关键字比较次数为:%d\n”,L->com);}
4.1.5 堆排序
核心思想
堆排序是一树形选择排序,在排序过程中,将 R[1…N] 看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。将原始记录序列建成一个堆,成为初始堆,并输出堆顶元素;调整剩余的记录序列,使之成为一个新堆,再输出堆顶元素;如此反复进行,当堆中只有一个元素时,整个序列的排序结束,输出的序列便是原始序列的非递减有序序列。在堆排序的过程中,主要负责两方面的工作:一是如何将原始记录序列构造成一个堆,即建立初始堆;二是输出堆顶元素后,如何将剩余记录整理成一个新堆。
核心代码
void HeapAdjust(Sqlist L, int s, int m)
{ //假设r[s+1…m]已经是堆,将r[s…m]调整为以r[s]为根的大根堆
int j;
RedType 4rc;
rc = L->r[s];
L->mov++;
for (j=2s; j<=m; j*=2) { // 沿key较大的孩子结点向下筛选
L->com++;
if (j
++j; // j为key较大的记录的下标
L->com++;
}
if (rc.key >= L->r[j].key) break; // rc应插入在位置s上
L->r[s] = L->r[j];
L->mov++;
s = j;
}
L->r[s] = rc; // 插入
L->mov++;}
void HeapSort(Sqlist *L) {
int i;
RedType rc;
for(i=L->length/2;i>0;i–) //把H->r[…]建成大顶堆
HeapAdjust ( L, i, L->length );
for(i=L->length;i>1;i–){
rc = L->r[i];
L->r[i] = L->r[1];
L->r[1] = rc;
L->mov+=3;
HeapAdjust(L,1,i-1); // 将H.r[1…i-1] 重新调整为大顶堆
}
printf(“堆排序后的排列顺序如下:\n”);
for(i=1;i<=L->length;i++)
printf("%d\t",L->r[i]);//输出排序后数组和相关数据
printf("\n关键字移动次数为:%d\n",L->mov);
printf(“关键字比较次数为:%d\n”,L->com);}
4.1.6 希尔排序
核心思想
先取一个小于 n 的整数 d1 作为第一个增量,把文件的全部记录分成 d1 个组。所有距离为 dl 的倍数的记录放在同一个组中。先在各组内进 行 直接插入排序 ;然后,取第二个增量 d2
void ShellInsert(Sqlist L,int dk) //希尔排序
{ //对顺序表L做一趟增量是dk的希尔插入排序
int i,j;
for(i=dk+1;i<=L->length;++i){
L->com++;
if(L->r[i].keyr[i-dk].key) //需将L.r[i]插入有序增量子表
{
L->r[0]=L->r[i]; // r[0]只是暂存单元,不是哨兵。暂存在L.r[0]
for(j=i-dk;j>0&&L->r[0].keyr[j].key;j-=dk)
{ L->r[j+dk]=L->r[j]; //记录后移,直到找到插入位置
L->mov++;
L->com++; }
L->r[j+dk]=L->r[0]; //将r[0]即原r[i],插入到正确位置
L->mov++; }}}
void ShellSort(Sqlist L,int dt[],int t)
{ //按增量序列dt[0…t-1]对顺序表作t趟希尔排序
int i,k;
for(k=0;k
图七 希尔排序
}
4.1.7 归并排序
核心思想
将有 n 个记录的原始序列看作 n 个有序子序列,每个子序列的长度为 1 ,然后从第一个子序列开始,把相邻的子序列两两合并,得到 [n/2] 个长度为 2 或 1 的子序列(当子序列个数为奇数时,最后一组合并得到的序列长度为 1 ),把这一过程称为一次归并排序,对第一次归并排序后的 [n/2] 个子序列采用上述方法继续顺序成对归并,如此重复,当最后得到的长度为 n 的一个子序列时,该子序列便是原始序列归并排序后的有序序列。
核心代码
void MergePass(int length,int x,int y)// 一次二路归并排序
{ int i;
for(i=1;i+2length-1<=L;i=i+2length)
{ Merge(i,i+length-1,i+2
}
if(i+length-1
}
}
// 归并排序
void Mergesort() // 二路归并排序
{ int length,k,m=0,i,x=0,y=0;
printf("\n\t\t 原始数据为(按回车键开始排序): \n\t\t");
for(k=1;k<=L;k++)
{ printf("%5d",R[k].key);
}
getchar();
printf("\n");
for(length=1;length
{ MergePass(length,&x,&y);
m++; // 输出语句包括排序的结果及次数
printf("\t\t 第 %d 趟归并排序的结果为: \n\t\t",m);
for(k=1;k<=L;k++)
{ printf("%5d",R[k].key);
}
getchar();
printf("\n");
}
printf("\n\t\t 排序最终结果是: \n\t\t");
for(i=1;i<=L;i++)
{ printf("%5d",R[i].key);
}
printf("\n");
printf("\t\t 比较次数: %d\n",x);
printf("\t\t 移动次数: %d\n",y);
}