排序算法的实现(必做,验证性实验,4学时)
- 实验目的
熟悉排序的方法、过程及原则。掌握插入排序、快速排序、选择排序、归并排序的算法思想及实现方法,掌握其时间复杂度的分析方法。
- 实验内容
定义待排序序列的存储结构。验证插入排序、希尔排序、冒泡排序、快速排序、简单选择排序、堆排序、归并排序中各排序方法中的一、二个排序算法。
- 插入类排序:直接插入排序、希尔排序
- 交换类排序:冒泡排序、快速排序
- 选择类排序:简单选择排序、堆排序
- 归并类排序:递归和非递归形式的归并排序(归并排序在下面的博客里会发,这一个就先不发了)
各个函数如下,整体可运行的代码在最后。
直接插入排序
void InsertSort(SqList &L){
int i,j;
for(i=2;i<=L.length;i++){
if(LT(L.base[i],L.base[i-1])){//逆序 需要移动
L.base[0] = L.base[i];//设置岗哨
L.base[i] = L.base[i-1];
for(j=i-2;LT(L.base[0],L.base[j]);j--)
L.base[j+1] = L.base[j];
L.base[j+1] = L.base[0];
}
}
}
希尔排序
void ShellInsert(SqList &L,int dk){//进行一次增量为dk的希尔排序
int i,j;
for(i=1+dk;i<=L.length;i++){
if(L.base[i],L.base[i-dk]){//从后向前依次比较
L.base[0] = L.base[i];
for(j=i-dk;j>0&<(L.base[0],L.base[j]);j-=dk)
L.base[j+dk] = L.base[j];
L.base[j+dk] = L.base[0];
}
}
}
void ShellSort(SqList &L,int dlta[],int t){//dlta[]数组里存放的是需要进行希尔排序的增量 t是dlta的表长
for(int k=0;k<t;k++)
ShellInsert(L,dlta[k]);//一趟增量为dlta[k]的插入排序
}
冒泡排序
void Bubble_Sort(SqList &L){
int change = true;
for(int i=1;i<=L.length-1&&change;i++){//n-1趟 如果内循环进行完了之后没有进行一次交换 说明后面都是有序的不需要再排序了可以直接退出
change = false;
for(int j=1;j<=L.length-i;j++){
L.base[0] = L.base[j+1];
if(L.base[j]>L.base[j+1]){
L.base[j+1] = L.base[j];
L.base[j] = L.base[0];
change = true;
}
}
}
}
快速排序
int Partition(SqList &L,int low,int high){//进行一次交换的函数
L.base[0] = L.base[low];//一般用子表的第一个记录作为枢轴
while(low<high){//low=high跳出循环 从表的两端交替的向中间扫描
while(low<high&&L.base[high]>=L.base[0]) high--;
L.base[low] = L.base[high];//将比枢轴记录小的记录移到低端
while(low<high&&L.base[low]<=L.base[0]) low++;
L.base[high] = L.base[low];//将比枢轴记录大的记录移到高端
}
L.base[low] = L.base[0];//枢轴记录到位
return low;
}
void QSort(SqList &L,int low,int high){
if(low<high){
int pivotloc = Partition(L,low,high);//因为Partition()函数并没有将low和high的值的改变带回 所以low和high都是原值
QSort(L,low,pivotloc-1);//对低字表递归排序 pivotloc是枢轴记录所在位置
QSort(L,pivotloc+1,high);//对高字表递归排序
}
}
简单选择排序
void SelectSort(SqList &L){
int min;
for(int i=1;i<=L.length-1;i++){
min = i;
L.base[0] = L.base[i];//备份
for(int j=i+1;j<=L.length;j++)
if(L.base[j]<L.base[min]) min = j;
if(i!=min){
L.base[i] = L.base[min];
L.base[min] = L.base[0];
}
}
}
堆排序
void HeadAdjust(SqList &L,int s,int m){//假设除了根之外的序列都是有序的 那么对根做一次筛选 使其插入在合适的位置 整体有序
L.base[0] = L.base[s];
for(int i=2*s;i<=m;i*=2){
if(i<m&<(L.base[i],L.base[i+1])) i++;//i停在子树里最大的孩子上
if(LT(L.base[i],L.base[0])) break;//根大于两个孩子里最大的 则这个子树是大根堆 不需要再作调整
L.base[s] = L.base[i];//孩子里数值大的上去做根
s = i; //s永远指向大根堆的根
}
L.base[s] = L.base[0];//插入
}
void HeadSort(SqList &L){
for(int i=L.length/2;i>0;i--) HeadAdjust(L,i,L.length);//把L.base[1...H.length]建成大根堆
for(int i=L.length;i>1;i--){
printf("%d ",L.base[1]);//输出堆顶元素
L.base[0] = L.base[1];//将堆顶记录和当前序列里的最后一个元素交换
L.base[1] = L.base[i];
L.base[i] = L.base[0];
HeadAdjust(L,1,i-1);//只有堆顶元素相对于子树来说无序 其余子树均是有序序列 只需要调用HeadAdjust算法重新调整1~i-1有序即可 i可无视掉
}
}
整体可运行的代码,main函数里调用不同的函数,使用不同的方法排序,但是注意要将别的排序注释掉,一次只能用一个。
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define true 1
#define false 0
#define LIST_INIT_SIZE 50
#define LISTINCREMENT 10
#define LT(a,b) a<b
#define EQ(a,b) a==b
#define LQ(a,b) a<=b
#define NULLKEY -111
typedef int ElemType;
typedef int Status;
typedef struct{
ElemType *base;
int length;
int listsize;
}SqList;
Status InitSqlist(SqList &L){
L.base = (ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.base) exit(-1);
L.length = 0;
L.listsize = LIST_INIT_SIZE;
}
Status CreatSqlist(SqList &L){
InitSqlist(L);
int i=1;
printf("请输入元素 输入-111意为停止:\n");
scanf("%d",&L.base[i]);
while(L.base[i]!=NULLKEY){
i++;
scanf("%d",&L.base[i]);
}
L.length = --i;
return 0;
}
void InsertSort(SqList &L){
int i,j;
for(i=2;i<=L.length;i++){
if(LT(L.base[i],L.base[i-1])){//逆序 需要移动
L.base[0] = L.base[i];//设置岗哨
L.base[i] = L.base[i-1];
for(j=i-2;LT(L.base[0],L.base[j]);j--)
L.base[j+1] = L.base[j];
L.base[j+1] = L.base[0];
}
}
}
void ShellInsert(SqList &L,int dk){
int i,j;
for(i=1+dk;i<=L.length;i++){
if(L.base[i],L.base[i-dk]){//从后向前依次比较
L.base[0] = L.base[i];
for(j=i-dk;j>0&<(L.base[0],L.base[j]);j-=dk)
L.base[j+dk] = L.base[j];
L.base[j+dk] = L.base[0];
}
}
}
void ShellSort(SqList &L,int dlta[],int t){//dlta[]数组里存放的是需要进行希尔排序的增量 t是dlta的表长
for(int k=0;k<t;k++)
ShellInsert(L,dlta[k]);//一趟增量为dlta[k]的插入排序
}
void Bubble_Sort(SqList &L){
int change = true;
for(int i=1;i<=L.length-1&&change;i++){//n-1趟 如果内循环进行完了之后没有进行一次交换 说明后面都是有序的不需要再排序了可以直接退出
change = false;
for(int j=1;j<=L.length-i;j++){
L.base[0] = L.base[j+1];
if(L.base[j]>L.base[j+1]){
L.base[j+1] = L.base[j];
L.base[j] = L.base[0];
change = true;
}
}
}
}
int Partition(SqList &L,int low,int high){//进行一次交换的函数
L.base[0] = L.base[low];//一般用子表的第一个记录作为枢轴
while(low<high){//low=high跳出循环 从表的两端交替的向中间扫描
while(low<high&&L.base[high]>=L.base[0]) high--;
L.base[low] = L.base[high];//将比枢轴记录小的记录移到低端
while(low<high&&L.base[low]<=L.base[0]) low++;
L.base[high] = L.base[low];//将比枢轴记录大的记录移到高端
}
L.base[low] = L.base[0];//枢轴记录到位
return low;
}
void QSort(SqList &L,int low,int high){
if(low<high){
int pivotloc = Partition(L,low,high);//因为Partition()函数并没有将low和high的值的改变带回 所以low和high都是原值
QSort(L,low,pivotloc-1);//对低字表递归排序 pivotloc是枢轴记录所在位置
QSort(L,pivotloc+1,high);//对高字表递归排序
}
}
void SelectSort(SqList &L){
int min;
for(int i=1;i<=L.length-1;i++){
min = i;
L.base[0] = L.base[i];//备份
for(int j=i+1;j<=L.length;j++)
if(L.base[j]<L.base[min]) min = j;
if(i!=min){
L.base[i] = L.base[min];
L.base[min] = L.base[0];
}
}
}
void Visit(SqList L){
printf("\n");
for(int i=1;i<=L.length;i++){
printf("%d ",L.base[i]);
}
}
void HeadAdjust(SqList &L,int s,int m){
L.base[0] = L.base[s];
for(int i=2*s;i<=m;i*=2){
if(i<m&<(L.base[i],L.base[i+1])) i++;//i停在子树里最大的孩子上
if(LT(L.base[i],L.base[0])) break;//根大于两个孩子里最大的 则这个子树是大根堆 不需要再作调整
L.base[s] = L.base[i];//孩子里数值大的上去做根
s = i; //s永远指向大根堆的根
}
L.base[s] = L.base[0];//插入
}
void HeadSort(SqList &L){
for(int i=L.length/2;i>0;i--) HeadAdjust(L,i,L.length);//把L.base[1...H.length]建成大根堆
for(int i=L.length;i>1;i--){
printf("%d ",L.base[1]);//输出堆顶元素
L.base[0] = L.base[1];//将堆顶记录和当前序列里的最后一个元素交换
L.base[1] = L.base[i];
L.base[i] = L.base[0];
HeadAdjust(L,1,i-1);//只有堆顶元素相对于子树来说无序 其余子树均是有序序列 只需要调用HeadAdjust算法重新调整1~i-1有序即可 i可无视掉
}
}
int main(){
SqList L;
int dlta[3]={5,3,1};
CreatSqlist(L);
printf("\n排序前的结果:");
Visit(L);
// printf("\n直接插入排序后的结果:");
// InsertSort(L);
// printf("\n希尔排序后的结果:");
// ShellSort(L,dlta,3);
// printf("\n冒泡排序后的结果:");
// Bubble_Sort(L);
// printf("\n快速排序后的结果:");
// QSort(L,1,L.length);
// printf("\n简单选择排序后的结果:");
// SelectSort(L);
Visit(L);
printf("\n堆排序后的结果:");
HeadSort(L);
return OK;
}