数据结构(c)——排序算法

来源:互联网 发布:mac如何获取最高权限 编辑:程序博客网 时间:2024/04/28 05:31

排序是将一个记录的任意序列,重新排列成一个按关键字有序的序列(按非递减或非递增)。

排序算法按排序过程中依据的不同原则可以大致分为五类:插入排序、交换排序、选择排序、归并排序、基数排序。

下面程序均使无序序列排序成非递减序列。

数据结构使用顺序表,并初始化8个元素 [49,38,65,97,76,13,27,49],

typedef struct  Record{int key;          //关键字项 char otherinfo;   //其他数据项 };   typedef struct SqList{Record *r;int length;};

main(){   SqList L;   InitList(L);   printf("排序前:");   for(int i = 1;i <= L.length;i ++){  printf("%d ",L.r[i].key);   }   printf("\n直接插入排序后:");   InsertSort(L);   for(int i = 1;i <= L.length;i ++){  printf("%d ",L.r[i].key);   }   printf("\n");}

一、插入排序

基本操作是将一个记录插入到已排好序的有序表中,得到一个新的有序表。
1、直接插入排序。最简单的排序方法。
算法思想:从数组第二个元素开始,将前面所有元素视为已排好序的有序表,在其中找到插入位置插入,重复过程直至结束。
void InsertSort(SqList &L){for(int i = 2;i <= L.length;i ++){if(L.r[i].key < L.r[i - 1].key){L.r[0] = L.r[i];           //复制为哨兵 ,大大减少比较次数 L.r[i] = L.r[i - 1];int j;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];}}}
2、折半插入排序。插入排序的操作可分为“查找”和“插入”,折半插入即使用折半查找来完成查找,能有效减少关键字的比较次数,记录移动次数不变。
void BInsertSort(SqList &L){//利用折半查找查找插入点 for(int i = 2;i <= L.length;i ++){L.r[0] = L.r[i];   //哨兵 int low = 1;int high = i - 1;while(low <= high){    //在r[low ... high]范围内进行折半查找 int m = (low + high) / 2;if(L.r[0].key < L.r[m].key){high = m - 1;}else{low = m + 1; } }//low 或 high+1 即为插入位置         for(int j = i - 1;j >= low;j --){L.r[j + 1] = L.r[j];}L.r[low] = L.r[0];}}
3、希尔排序。算法思想:设置一个增量,将无序序列以这个增量为间隔分成若干个子序列,分别进行插入排序,缩小增量重复操作,直到整个序列中的记录基本有序时,即增量缩小至1时,对全体记录进行一次直接插入排序。时间效率大大提高。
void ShellInsert(SqList &L,int dk){      //一趟希尔排序 //对直接插入排序作修改,前后记录的增量是dk而不是1//L.r[0]不作为哨兵,仅暂存元素用 for(int i = dk + 1;i <= L.length;i ++){if(L.r[i].key < L.r[i - dk].key){L.r[0] = L.r[i];int j;for(j = i - dk;j > 0 && L.r[0].key < L.r[j].key;j -= dk){L.r[j + dk] = L.r[j];}L.r[j + dk] = L.r[0];}} } void ShellSort(SqList &L,int dlta[],int t){   //希尔排序 for(int k = 0;k < t;k ++){ShellInsert(L,dlta[k]);}} //如int a[3] = {5,3,1};//ShellSort(L,a,3); 
上述排序算法运行结果:
  

二、交换排序

基本操作是将两个元素交换。比较简单的是"冒泡排序",从头比较相邻的两个记录,将较大的移到后面,每一趟将剩余序列最大的数移至末尾,大数像石头一下沉到底,小数像泡泡慢慢冒起。。
快速排序:对冒泡排序的改进。算法思想:设置一个枢纽,通过一趟排序将序列分割成两部分,一部分的关键字均比枢纽小,一部分均比枢纽大,再分别对两部分记录进行排序,达到整个序列有序。
int Partition(SqList &L,int low,int high){   //一趟快速排序,并将枢纽下标返回 int pivokey = L.r[low].key;   //用子表第一个元素作枢纽 L.r[0] = L.r[low];     //暂存枢纽while(low < high){while(low < high && L.r[high].key >= pivokey){high --;} L.r[low] = L.r[high];while(low < high && L.r[low].key <= pivokey){low ++;}L.r[high] = L.r[low];} L.r[low] = L.r[0];  //low == high return low;}void QSort(SqList &L,int low,int high){if(low < high){int pivotloc = Partition(L,low,high);QSort(L,low,pivotloc - 1);QSort(L,pivotloc + 1,high); }}
运行结果:


三、选择排序

选择排序的基本思想是每一趟在n-i+1(i=1,2,...,n-1)个记录中选择关键字最小的记录作为序列中第i个记录。
1、简单选择排序:进行n-1趟选择。
int SelectMinKey(SqList L,int i){   //从L[i..L.length]中选出关键字最小的记录下标返回 int min = i;for(int j = i + 1;j <= L.length;j ++){   if(L.r[j].key < L.r[min].key){min = j;}}return min;}void SelectSort(SqList &L){for(int i = 1;i < L.length;i ++){int j = SelectMinKey(L,i);if(i != j){Record record;record = L.r[i];L.r[i] = L.r[j];L.r[j] = record;}}}
 
2、堆排序
堆:n个元素的序列(k1,k2,...,kn)当且仅当满足下面关系时,称之为堆。
所有ki <= k2i, ki <= k(2i+1),    或     所有ki >= k2i, ki >= k(2i+1)        i=1,2,...,n/2。        将保存堆的数组看作完全二叉树 ,则所有非终端结点都小于等于其左右孩子结点的值(或大于等于),该完全二叉树的根(堆顶元素)必为序列中的最小值(或最大值)。
堆排序算法思想: 由无序序列建立成堆,输出堆顶元素(根结点),调整剩余元素成为一个新的堆,重复操作。
void HeapAdjust(SqList &L,int s,int m){   //使L[s,...,m]成为大顶堆,大的结点在上 Record rc = L.r[s];for(int j = 2 * s;j <= m;j *= 2){   //沿key较大的孩子结点向下筛选,另一边不用管 if(j < m && L.r[j].key < L.r[j + 1].key){    //j为key较大的孩子结点下标 j ++;}if(rc.key >= L.r[j].key){    //已经比两孩子结点都大,不需再筛选,跳出循环 break;} L.r[s] = L.r[j];s = j;}L.r[s] = rc;}void HeapSort(SqList &L){for(int i = L.length/2;i > 0;i --){   //建立"大顶堆" HeapAdjust(L,i,L.length);}for(int i = L.length;i > 1;i --){Record record = L.r[1];  //最大元素移至最后 L.r[1] = L.r[i];L.r[i] = record;HeapAdjust(L,1,i - 1);}} 
运行结果:  

四、归并排序

"归并"含义是将两个或多个有序表组合成一个新的有序表。常用的有2-路归并排序,将序列不断分为两半,直到得到长度为1的序列,开始两两归并,直到结束,看出可用递归算法。
void Merge(SqList L,SqList &H,int i,int m,int n){//将L[i,...,m] 和 L[m+1,...,n]归并为有序的H[i,...,n]int j,k;for(j = m + 1,k = i;i <= m && j <= n; k++){if(L.r[i].key < L.r[j].key){H.r[k] = L.r[i ++];}else{H.r[k] = L.r[j ++]; } }for(;i <= m;i ++,k++){H.r[k] = L.r[i];}for(;j <= n;j ++,k++){H.r[k] = L.r[j];}}void MSort(SqList L,SqList &H,int s,int t){  SqList G;InitList(G);if(s == t) H.r[s] = L.r[s];else{int m = (s + t) / 2;MSort(L,G,s,m);//递归地将左边归并到中间变量 MSort(L,G,m + 1,t);Merge(G,H,s,m,t);//将中间变量左右归并回序列 }}void MergeSort(SqList &L){MSort(L,L,1,L.length); }
运行结果:

五、基数排序

借助多关键字排序,balabalabalabalabalabalabalabala



 

1 0
原创粉丝点击