数据结构之排序算法

一、 实验目的

了解排序的相关概念,理解各种排序方法的思想与排序过程,掌握各种排序方法的时间复杂度分析,实现各种排序算法。

二、 实验内容

通过编写程序,实现希尔排序、快速排序、堆排序和归并排序等算法。具体步骤如下:

  1. 在主函数中输入线性序列和关键字;
  2. 创建实现希尔排序、快速排序、堆排序和归并排序的子函数;
  3. 在主函数中,通过switch语句调用相关函数,实现排序。

三、 实验工具

Dev - C++

四、 实验代码

//Authors:xiaobei

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 20
typedef int KeyType;
typedef char InfoType;
typedef struct{
 KeyType key;
 InfoType otherinfo;
}RedType;
typedef struct{
 RedType r[MAXSIZE+1];
 int length;
}SqList;
//函数创建顺序表
int CreateSq(SqList &L){
 int i,length;
 printf("\n请输入待排序元素个数\n>>>");
 scanf("%d",&length);
 if(length<=MAXSIZE){
  for(i=1;i<=length;i++){
   printf("\n请依次输入关键字项:\n>>>");
   scanf("%d",&L.r[i].key);
   L.r[i].otherinfo = NULL;
  }
  L.length = length;
 }
 else{
  printf("\n【超出容量!!!】\n");
  return 0;
 }
 return 1;
}
//函数展示排序后的结果
void show(SqList L){
 int i;
 for(i=1;i<=L.length;i++)
  printf("%d  ",L.r[i].key);
}
/**************************直接插入排序**************************/
int InsertSort(SqList &L){
   //对顺序表L做直接插入排序
 int i,j;
 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],插入到正确位置
  }           //if
  return 1;
}
/**************************折半插入排序**************************/
int BInsertSort(SqList &L){
 //对顺序表L做折半插入排序
 int i,j,low,high,m;
 for(i=2;i<=L.length;++i)
 {
  L.r[0]=L.r[i];               //将待插入的记录暂存到监视哨中
  low=1; high=i-1;             //置查找区间初值
  while(low<=high)
  {           //在r[low..high]中折半查找插入的位置
   m=(low+high)/2;                //折半
   if(L.r[0].key<L.r[m].key)  high=m-1; //插入点在前一子表
   else  low=m+1;          //插入点在后一子表
  }//while
  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],插入到正确位置
    }
 return 1;
}
/**************************希尔排序**************************/
void ShellInsert(SqList &L,int dk){
 //对顺序表L做一趟增量是dk的希尔插入排序
 int i,j;
 for(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];    //记录后移dk,直到找到插入位置
   L.r[j+dk]=L.r[0];     //将r[0]即原r[i],插入到正确位置
  }
}
int ShellSort(SqList &L,int dt[ ],int t){
   //按增量序列dt[0..t-1]对顺序表L作t趟希尔排序
 int k;
    for(k=0;k<t;++k)
        ShellInsert(L,dt[k]);      //一趟增量为dt[t]的希尔插入排序
 return 1;
}
/**************************冒泡排序**************************/
int BubbleSort(SqList &L){
 //对顺序表L做冒泡排序
 int m,j,flag;
 RedType t;
    m=L.length-1; flag=1;     //flag用来标记某一趟排序是否发生交换
    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;
    }
 return 1;
}
/**************************简单选择排序**************************/
int SelectSort(SqList &L) 
{ 
   //对顺序表L做简单选择排序
 int i,j,k;
 RedType t;
    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) {t=L.r[i];L.r[i]=L.r[k];L.r[k]=t;} //交换r[i]与r[k]        
     }            //for  
 return 1;
}
/**************************快速排序**************************/
int Partition(SqList &L,int low,int high){ 
 //对顺序表L中的子表r[low..high]进行一趟排序,返回枢轴位置
 int pivotkey;
 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];     //将比枢轴记录大的记录移到高端
 }//while
 L.r[low]=L.r[0];      //枢轴记录到位
 return  low;       //返回枢轴位置
}
void QSort(SqList &L,int low,int high){ //调用前置初值:low=1; high=L.length;
    //对顺序表L中的子序列L.r[low..high]做快速排序
 int pivotloc;
    if(low<high)
 {          //长度大于1
       pivotloc=Partition(L,low,high);   //将L.r[low..high]一分为二,pivotloc是枢轴位置
       QSort(L,low,pivotloc-1);    //对左子表递归排序
       QSort(L,pivotloc+1,high);         //对右子表递归排序
 }
}           
int QuickSort(SqList &L)
{
   //对顺序表L做快速排序
   QSort(L,1,L.length);      //调用前置初值:low=1; high=L.length;
   return 1;
}
/**************************堆排序**************************/
//函数调整堆
void HeapAdjust(SqList &L,int s,int m){ 
   //假设r[s+1..m]已经是堆,将r[s..m]调整为以r[s]为根的大根堆
 RedType rc;
 int j;
 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;                             //插入
}            
//函数建初堆
void CreatHeap(SqList &L){
 //把无序序列L.r[1..n]建成大根堆
 int i,n;
 n=L.length;
 for(i=n/2;i>0;--i)            //反复调用HeapAdjust 
  HeapAdjust(L,i,n);
}   
//函数堆排序
int HeapSort(SqList &L) { 
 //对顺序表L进行堆排序 
 int i;
 CreatHeap(L);                   //把无序序列L.r[1..L.length]建成大根堆 
 for(i=L.length;i>1;--i)
 { 
  L.r[0]=L.r[1];                   //将堆顶记录和当前未经排序子序列L.r[1..i]中最后一个记录互换 
  L.r[1]=L.r[i];            
  L.r[i]=L.r[0]; 
  HeapAdjust(L,1,i-1);     //将L.r[1..i-1]重新调整为大根堆 
   }
 return 1;
}
/**************************二路归并排序**************************/
//函数合并两个有序子序列
void Merge(RedType R[],RedType T[],int low,int mid,int high){ 
   //将有序表R[low..mid]和R[mid+1..high]归并为有序表T[low..high] 
 int i,j,k;
 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(i<=mid)                              //将剩余的R[low..mid]复制到T中 
  T[k++]=R[i++];                 
 while(j<=high)                             //将剩余的R[j.high]复制到T中 
  T[k++]=R[j++];                       
}
//函数递归实现归并排序
void MSort(RedType R[],RedType T[],int low,int high)
{ 
 //R[low..high]归并排序后放入T[low..high]中 
 int mid;
 RedType *S=new RedType[MAXSIZE];
    if(low==high) T[low]=R[low]; 
    else
 { 
  mid=(low+high)/2;            //将当前序列一分为二,求出分裂点mid 
        MSort(R,S,low,mid);       //对子序列R[low..mid] 递归归并排序,结果放入S[low..mid] 
        MSort(R,S,mid+1,high);      //对子序列R[mid+1..high] 递归归并排序,结果放入S[mid+1..high] 
        Merge(S,T,low,mid,high);     //将S[low..mid]和S [mid+1..high]归并到T[low..high]  
    }
} 
int MergeSort(SqList &L)
{ 
 //对顺序表L做归并排序 
    MSort(L.r,L.r,1,L.length);
 return 1;
}
void Menu(){
 printf("\n———————————— 菜单 ——————————————");
 printf("\n1.创建顺序表;\t2.直接插入排序;\t3.折半插入排序;\n");
 printf("\n4.希尔排序;\t5.冒泡排序;\t\t6.简单选择排序;\n");
 printf("\n7.快速排序;\t8.堆排序;\t\t9.二路归并排序;\n");
 printf("\n\t    10.显示排序结果;\t0.退出;\n");
 printf("—————————————————————————————\n");
 printf("请输入你的选择:\n>>>");
}
/**************************主函数**************************/
int main(){
 int user,dt[3] = {5,3,1};
 SqList L;
 while(true){
  Menu();
  scanf("%d",&user);
  switch(user){
  case 1:{
   if(CreateSq(L))
    printf("\n【顺序表创建成功!!!】\n");
   else
    printf("\n【顺序表创建失败!!!】\n");
   break;
      }
  case 2:{
   if(InsertSort(L))
    printf("\n【直接插入排序成功!!!】\n");
   break;
      }
  case 3:{
   if(BInsertSort(L))
    printf("\n【折半插入排序成功!!!】\n");
   break;
      }
  case 4:{
   if(ShellSort(L,dt,3))
    printf("\n【希尔排序成功!!!】\n");
   break;
      }
  case 5:{
   if(BubbleSort(L))
    printf("\n【冒泡排序成功!!!】\n");
   break;
      }
  case 6:{
   if(SelectSort(L))
    printf("\n【简单选择排序成功!!!】\n");
   break;
      }
  case 7:{
   if(QuickSort(L))
    printf("\n【快速排序成功!!!】\n");
   break;
    }
  case 8:{
   if(HeapSort(L))
    printf("\n【堆排序成功!!!】\n");
   break;
      }
  case 9:{
   if(MergeSort(L))
    printf("\n【二路归并排序成功!!!】\n");;
   break;
      }
  case 10:{
   show(L);
   break;
    }
  case 0:exit(0);
  }
 }
 return 0;
}


五、 实验结果

  1. 创建顺序表

1
2
2. 排序(显示部分)

3

  1. 显示排序结果

4

  1. 退出

5

六、 总结与思考

(搬运)

6

分类:
1.插入
直接插入排序、折半插入排序、希尔排序
2.交换
冒泡排序、快速排序
3.选择
简单选择排序、堆排序
4.归并
二路归并排序、多路归并排序

7
(部分内容)