归并排序

【1】归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法也是采用分治法(Divide and Conquer)的一个非常典型的应用。

归并排序的算法复杂度为O(N*logN)。

归并排序算法是稳定的(参见随笔《常用排序算法稳定性分析》)。

【2】归并排序逻辑分析与代码实现

在分析归并排序的逻辑之前,让我们也利用一下分治法理念:先从基层做起(个人之拙见)。

先考虑一个简单问题:如何将两个有序数列进行合并?(注意:已有序数列)

好吧!其实,这个简单的问题会给我们很大的启迪。步骤整理如下:

<1>只要把两个待合并数列的第一个数据进行比较,哪个小就先安置哪个,排序之后就在对应数列中跳过该数据索引(下标)。

<2>重复以上过程,直至有一个数列已经完全安置(即已为空)。

<3>再将另一个数列(未空数列)的所剩数据直接取出即可。

(1)示例代码如下:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 //将有序数组ar[]和br[]合并到cr[]中
 5 void MemeryArray(int a[], int n, int b[], int m, int c[])
 6 {
 7     int i, j, k;
 8 
 9     i = j = k = 0;
10     while (i < n && j < m)
11     {
12         if (a[i] < b[j])
13             c[k++] = a[i++];
14         else
15             c[k++] = b[j++]; 
16     }
17 
18     while (i < n)
19         c[k++] = a[i++];
20 
21     while (j < m)
22         c[k++] = b[j++];
23 }
24 
25 void  PrintArr(int ar[],int n)
26 {
27     for(int i = 0; i < n; ++i)
28         cout<<ar[i]<<" ";
29     cout<<endl;
30 }
31 
32 void main()
33 {
34     int ar[5] = {12, 23, 34, 45, 56};
35     int br[5] = {13, 24, 35, 46, 60};
36     int cr[10];
37     cout<<"数组ar为:"<<endl;
38     PrintArr(ar, 5);
39     cout<<"数组br为:"<<endl;
40     PrintArr(ar, 5);
41     MemeryArray(ar, 5, br, 5, cr);
42     cout<<"合并后结果为:"<<endl;
43     PrintArr(cr, 10);
44 }
45 
46 /*
47 数组ar为:
48 12 23 34 45 56
49 数组br为:
50 12 23 34 45 56
51 合并后结果为:
52 12 13 23 24 34 35 45 46 56 60
53 */

可以看出合并有序数列的效率是比较高的,完全可以达到O(n)。

那么,解决了上面的合并有序数列问题之后,我们再来看归并排序。

归并排序的基本思路就是先将待排序数组分成两组A,B,然后如果这两组组内的数据都是有序的,就可以利用上面的逻辑(合并有序数列逻辑)很方便的将这两个有序数组数据再进行合并排序。

问题关键是如何让这两组组内数据有序呢?

可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的两个小组就可以了。

这样通过先递归的分解待排序数列,再合并数列就完成了归并排序的过程。实现归并排序。

仔细想,仔细想,仔细想,先想明白这几句话,再看下面的代码。

(2)归并排序实现及测试示例代码:

 1 #include<iostream>
 2 using namespace std;
 3 
 4 #define  MAXSIZE  10
 5 
 6 //将两个有序数列a[first...mid] 和 a[mid...last] 合并。
 7 void mergearray(int a[], int first, int mid, int last, int temp[])
 8 {
 9     int i = first, j = mid + 1;
10     int m = mid, n = last;
11     int k = 0;
12 
13     while (i <= m && j <= n)
14     {
15         if (a[i] <= a[j])
16             temp[k++] = a[i++];
17         else
18             temp[k++] = a[j++];
19     }
20 
21     while (i <= m)
22         temp[k++] = a[i++];
23 
24     while (j <= n)
25         temp[k++] = a[j++];
26 
27     for (i = 0; i < k; ++i)
28         a[first + i] = temp[i];
29 }
30 void mergesort(int a[], int first, int last, int temp[])
31 {
32     if (first < last)
33     {
34         int mid = (first + last) / 2;
35         mergesort(a, first, mid, temp);     //左边有序
36         mergesort(a, mid + 1, last, temp);  //右边有序
37         mergearray(a, first, mid, last, temp); //再将两个有序数列合并
38     }
39 }
40 
41 bool MergeSort(int a[], int n)
42 {
43     int *p = new int[n];
44     if (p == NULL)
45         return false;
46     mergesort(a, 0, n - 1, p);
47     delete[] p;
48     return true;
49 }
50 
51 void  PrintArr(int ar[],int n)
52 {
53     for(int i = 0; i < n; ++i)
54         cout<<ar[i]<<" ";
55     cout<<endl;
56 }
57 
58 void main()
59 {
60     int ar[MAXSIZE] = {23, 34, 45, 78, 90, 12, 49, 92, 32, 19};
61     PrintArr(ar, MAXSIZE);
62     bool bValue = MergeSort(ar, MAXSIZE);
63     if(!bValue)
64     {
65         cout<<"MergeSort  Failed!! "<<endl;
66     }
67     PrintArr(ar, MAXSIZE);
68 }

以上内容参考文章:白话经典算法系列之五 归并排序的实现

(3)另外一种代码实现:

 1 #include<iostream>
 2 #include<malloc.h>
 3 using namespace std;
 4 
 5 #define   MAXSIZE  10
 6 
 7 void  PrintArr(int ar[],int n)
 8 {
 9     for(int i = 0; i < n; ++i)
10         cout<<ar[i]<<" ";
11     cout<<endl;
12 }
13 
14 static void merge(int ar[], int low, int mid, int high)  
15 {  
16     int i, k = 0;  
17     //申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列   
18     int *temp = (int *)malloc((high - low + 1)*sizeof(int));   
19     int begin1 = low;  
20     int end1 = mid;
21 
22     int begin2 = mid + 1;  
23     int end2 = high;   
24 
25     //比较两个元素,选择相对小的元素放入到合并空间,并移动指针到下一位置   
26     for (k = 0; begin1 <= end1 && begin2 <= end2;)    
27     {  
28         if(ar[begin1] < ar[begin2])  
29             temp[k++] = ar[begin1++];  
30         else  
31             temp[k++] = ar[begin2++];    
32     }   
33 
34     while(begin1 <= end1)  //若第一个序列有剩余,直接拷贝出来粘到合并序列尾   
35         temp[k++] = ar[begin1++];  
36     while(begin2 <= end2)  //若第二个序列有剩余,直接拷贝出来粘到合并序列尾   
37         temp[k++] = ar[begin2++];  
38 
39     for (i = 0;i < k; i++)   //将排序好的序列拷贝回数组中  
40     {
41         ar[low+i] = temp[i]; 
42     }
43            
44     free(temp);  
45 }  
46 void merge_sort(int ar[],int begin,int end)  
47 {  
48     int mid = 0;  
49     if(begin < end)  
50     {  
51         mid = (begin + end) / 2;
52         merge_sort(ar, begin, mid); 
53         merge_sort(ar, mid + 1, end);
54         merge(ar, begin, mid, end);  
55     }  
56 }  
57 
58 void  main()
59 {
60     int  ar[] = {12, 14, 54, 5, 6, 3, 9, 8, 47, 89};
61     merge_sort(ar, 0, MAXSIZE-1);
62     PrintArr(ar, MAXSIZE);
63 }
64 
65 /*
66 *3 5 6 8 9 12 14 47 54 89
67  */

推荐掌握第一种。第二种仅仅助于理解归并排序思想。当然,实现方式很多,那种好理解就使用那种,因人而异。

Good Good Study, Day Day Up.

顺序  选择  循环  坚持  总结 

作者:kaizen
声明:本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此声明,且在文章明显位置给出本文链接,否则保留追究法律责任的权利。
签名:顺序 选择 循环
原文地址:https://www.cnblogs.com/Braveliu/p/2860456.html