排序算法之归并排序
1)基本思想
归并排序的基本思想是将两个已经排好序的数组通过N-1次比较,归并到一个新的数组中,N是两个数组的元素个数之和。
比如数组:24 13 26 1 2 27 38 15,归并的方法是分别对数组的前四个数据和后四个数据排序,然后合并这两个部分排序的结果,而对前四个数据和后四个数据排序的方法也是通过将其分别分为两个小数组,然后合并的方法,即递归实现,这就是所谓的分而治之的思想。
2)算法实现
每一次合并两个已排序的数组时,需要一个新的包含两个数组总大小的空间来放置排序的结果,由于是递归实现,如果在每一次递归的过程中,都分配一段临时空间然后在该次排序结束时释放掉,那么这个分配和释放将占用很多时间。为了解决这个问题,可以在排序开始处分配一段与原始数组同等大小的空间用来放置各次递归的排序结果,然后在排序完全结束后释放掉这段空间。
代码如下:
1 #include "stdafx.h" 2 #include <iostream> 3 using namespace std; 4 5 void Merge(int* A, int* tempArray, int left, int center, int right) 6 { 7 int start = left; 8 int rleft = center + 1; 9 int tleft = left; 10 while(left <= center && rleft <= right) 11 { 12 if(A[left] <= A[rleft]) 13 { 14 tempArray[tleft] = A[left++]; 15 } 16 else 17 { 18 tempArray[tleft] = A[rleft++]; 19 } 20 tleft++; 21 } 22 while(left <= center) 23 { 24 tempArray[tleft++] = A[left++]; 25 } 26 while(rleft <= right) 27 { 28 tempArray[tleft++] = A[rleft++]; 29 } 30 31 for(; start <= right; start++) 32 { 33 A[start] = tempArray[start]; 34 } 35 } 36 37 void Recursion(int* A, int* tempArray, int left, int right) 38 { 39 if(left < right) 40 { 41 int center = (left + right)/2; 42 Recursion(A, tempArray, left, center); 43 Recursion(A, tempArray, center+1, right); 44 Merge(A, tempArray, left, center, right); 45 } 46 } 47 48 void print(int* A, int n) 49 { 50 for(int i = 0; i < n; i++) 51 { 52 cout<<A[i]<<" "; 53 } 54 cout<<endl; 55 } 56 57 void mergeSort(int A[], int n) 58 { 59 int* tempArray = new int[n]; 60 Recursion(A, tempArray, 0, n-1); 61 delete [] tempArray; 62 } 63 64 int _tmain(int argc, _TCHAR* argv[]) 65 { 66 int A[8] = {24,13,26,1,2,27,38,15}; 67 print(A, 8); 68 mergeSort(A, 8); 69 print(A, 8); 70 71 system("pause"); 72 return 0; 73 }
3)复杂度分析
归并排序使用的递归方法,最后一次的归并过程中最多需要N-1次比较,可以这样理解,每一次比较临时数组中都会多一个元素,由于共有N个元素,最后一个元素不需要比较,所以最多需要N-1次比较,假设N为2的幂,那么时间复杂度:
T(1) = 1;
T(n) = 2T(n/2) + n;
T(n)/n = T(n/2) /(n/2) + 1;
T(n/2) /(n/2) = T(n/4) /(n/4) + 1;
...
T(2)/2 = T(1)/1 + 1;
各式相加: T(n)/n = T(1)/1 + logn,最终,T(n) = O(nlogn).
归并排序很少用在主存排序中,其主要原因是归并排序需要附加与原始数组等大小的内存,同时还要来来回回将元素拷贝到临时数组中,并将其拷贝回来,因而增加了时空消耗。