算法之归并排序的递归与非递归的实现

时间:2021-10-15 04:12:57

一.什么是归并排序

    归并排序就是将多个有序的数据段合成一个有序的数据段,如果参与合并的只有两个有序的数据段,则称为二路归并。与快速排序和堆排序相比,其最大的特点是一种稳定的算法,算法的平均时间复杂度O(nlog2n)。

二.归并排序的基本思路

    (1).对于一个原始的待排序表,可以将R[1]到R[n]可以看做是n个长度为1的有序表,即分解。

    (2).进行第一趟归并,即将上述的n个子序两两合并,得到 n/2向上取整 个有序表,若n为奇数,则归并到最后一个子序列长度为1,即合并。

    (3).再将两个 n/2向上取整 个有序表两两合并,如此重复下去,直到得到一个长度为n的有序表,这种排序方法也叫2-路归并排序。

     原理如下图

 

                                         算法之归并排序的递归与非递归的实现

                                                      以[63,95,84,46,18,24,27,31,46]为例

三. 实现归并排序之前,需要调用"一次归并“和”一趟归并“,那什么是一次归并?什么是一趟归并呢?

     (1).一次归并:把首尾相接的两个有序表R[low...mid]、R[mid+1...high]归并成有序表R[low...high].

      (2) .一趟归并:把一些相互连接的有序表依次两两归并成一些更大的有序表。

 

 1.一次归并代码如下

//一次归并排序 
void Merge(int low,int m,int high)
{
// 将两个有序的子文件R[low..m)和R[m+1..high]归并成一个有序的子文件R[low..high]
int i=low;
int j=m+1;
int p=0; //置初始值
int *R1; //R1是局部向量
R1=(int *)malloc((high-low+1)*sizeof(int));
if(!R1) //申请空间失败
{
puts(
"空间申请失败");
return;
}
while(i<=m&&j<=high) // 两子文件非空时取其小者输出到R1[p]上
R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];
while(i<=m) // 若第1个子文件非空,则复制剩余记录到R1中
R1[p++]=R[i++];
while(j<=high) // 若第2个子文件非空,则复制剩余记录到R1中
R1[p++]=R[j++];
for(p=0,i=low;i<=high;p++,i++)
R[i]
=R1[p]; // 归并完成后将结果复制回R[low..high]
}

2.一趟归并排序代码如下

 1 //一趟归并排序 
2 void mergepass(int n,int len)
3 {
4 int i,t;
5 i=1;
6 while(i<=n-2*len+1)
7 {
8 Merge(i,i+len-1,i+2*len-1);
9 i=i+2*len;
10 }
11 if(i+len-1<n)
12 Merge(i,i+len-1,n);
13 }

 

   写完”一次归并“和"一趟归并”后,接下来就是编写二路归并了,二路归并就是调用“一次归并”和“一趟归并”,最后组合成一个长度为n的有序表。

二路归并有递归的方法和非递归的方法(即迭代方法),下面就来看一下递归与非递归的实现

(1)非递归的方法

 1 void mergesort(int n)       //非递归的二路归并实现
2 {
3 int len;
4 int *R1; // R1是局部向量
5 len=1;
6 while(len<n)
7 {
8 mergepass(n,len);
9 len=2*len;
10 }
11 }

(2)递归方法实现

 1 //递归的二路归并排序 
2 void MergeSortDC(int low,int high)
3 { //用分治法对R[low..high]进行二路归并排序
4 int mid;
5 if(low<high)
6 { // 区间长度大于1
7 mid=(low+high)/2; //分解 */
8 MergeSortDC(low,mid); // 递归地对R[low..mid]排序
9 MergeSortDC(mid+1,high); //递归地对R[mid+1..high]排序
10 Merge(low,mid,high); // 组合,将两个有序区归并为一个有序区
11 }
12 }

     讲到这里,归并排序的思想方法就讲完了,然后就可以自己写main()函数了,然后调用上面的自定义函数就可以实现了

下面是完整的代码实现,仅供参考,如有问题,欢迎指教

  1 #include <stdio.h>  
2 #include<malloc.h>
3 #include<stdlib.h>
4 #define MAX 100
5 int R[MAX];
6 //一次归并排序
7 void Merge(int low,int m,int high)
8 { // 将两个有序的子文件R[low..m)和R[m+1..high]归并成一个有序的子文件R[low..high]
9 int i=low;
10 int j=m+1;
11 int p=0; //置初始值
12 int *R1; //R1是局部向量
13 R1=(int *)malloc((high-low+1)*sizeof(int));
14 if(!R1) //申请空间失败
15 {
16 puts("空间申请失败");
17 return;
18 }
19 while(i<=m&&j<=high) // 两子文件非空时取其小者输出到R1[p]上
20 R1[p++]=(R[i]<=R[j])?R[i++]:R[j++];
21 while(i<=m) // 若第1个子文件非空,则复制剩余记录到R1中
22 R1[p++]=R[i++];
23 while(j<=high) // 若第2个子文件非空,则复制剩余记录到R1中
24 R1[p++]=R[j++];
25 for(p=0,i=low;i<=high;p++,i++)
26 R[i]=R1[p]; // 归并完成后将结果复制回R[low..high]
27 }
28 //递归的二路归并排序
29 void MergeSortDC(int low,int high)
30 { //用分治法对R[low..high]进行二路归并排序
31 int mid;
32 if(low<high)
33 { // 区间长度大于1
34 mid=(low+high)/2; //分解 */
35 MergeSortDC(low,mid); // 递归地对R[low..mid]排序
36 MergeSortDC(mid+1,high); //递归地对R[mid+1..high]排序
37 Merge(low,mid,high); // 组合,将两个有序区归并为一个有序区
38 }
39 }
40 //一趟归并排序
41 void mergepass(int n,int len)
42 {
43 int i,t;
44 i=1;
45 while(i<=n-2*len+1)
46 {
47 Merge(i,i+len-1,i+2*len-1);
48 i=i+2*len;
49 }
50 if(i+len-1<n)
51 Merge(i,i+len-1,n);
52 }
53 void mergesort(int n)
54 {
55 int len;
56 int *R1; // R1是局部向量
57 len=1;
58 while(len<n)
59 {
60 mergepass(n,len);
61 len=2*len;
62 }
63 }
64 void main()
65 {
66 int i,n,d;
67 puts("请输入数组的长度:");
68 scanf("%d",&n);
69 if(n<=0||n>MAX)
70 {
71 printf("n 必须大于 0 小于 %d.\n",MAX);
72 exit(0);
73 }
74 puts("请依次输入数组的数据:");
75 for(i=1;i<=n;i++)
76 scanf("%d",&R[i]);
77 puts("排序前的数组数据为:");
78 for(i=1;i<=n;i++)
79 printf("%4d",R[i]);
80 printf("\n\n 递归与非递归的合并排序 \n");
81 printf("\n 1.递归合并排序 \n");
82 printf("\n 2.非递归合并排序 \n");
83 printf("\n请输入您的选择:");
84 scanf("%d",&d);
85 if(d==1)
86 {
87 MergeSortDC(1,n);
88 puts("\n递归归并排序后:");
89 for(i=1;i<=n;i++)
90 printf("%4d",R[i]);
91 }
92 else if(d==2)
93 {
94 mergesort(n);
95 puts("\n非递归归并排序后:");
96 for(i=1;i<=n;i++)
97 printf("%4d",R[i]);
98 }
99 else
100 printf("您输入的菜单号有误!");
101 }