众所周知,归并排序的时间复杂度为O( nlogn ),在此就具体不表了,可以查看其他博客详解。
在这里主要是对一些刚学递归算法并对递归算法不太了解因而对递归分以及合并过程不了解同学进行讲解。
对于一个数字序列,要对其进行排序,按照分的操作就是不断将其往下分,在这里提供了两个递归函数,分别是对左边一半进行递归以及对右边一半进行递归,直到最后只有一个元素无法继续递归的时候返回到上一层,那么关键点就来了,也就是合并过程。(后续加上图片,更加形象)
对于合并的操作,是由小到大的规模进行排序的,这也是很多递归算法的一个特点(由小到大),在合并过程中,我刚开始不太理解的时合并时的端点是怎么处理的,后来想出了使用递归树进行模拟,很快就明白了。每层的左右端点都是固定的(这个可能有点不好理解),也就是说在由下面的一层排好序返回到上面这层的时候继续排序时两端是正确的。这时候继续执行之前的操作就行了。到最后就是整个数列都有序排列了。(后续补图)
归并排序模板:
1 #include <iostream> 2 using namespace std; 3 4 const int N = 100010; 5 int a[N], temp[N]; 6 7 void mergesort(int a[], int l, int r){ 8 //递归终止:当只有一个元素时返回 9 if(l == r) 10 return; 11 12 //取中点 13 int mid = l + r >> 1; 14 15 //对半递归 16 mergesort(a, l, mid); 17 mergesort(a, mid + 1, r); 18 19 //排序过程 20 int i = l, j = mid + 1, k = 0; 21 while(i <= mid && j <= r){ 22 if(a[i] <= a[j]) 23 temp[k ++] = a[i ++]; 24 else 25 temp[k ++] = a[j ++]; 26 } 27 28 //对非空的数组进行复制 29 while(i <= mid) 30 temp[k ++] = a[i ++]; 31 while(j <= r) 32 temp[k ++] = a[j ++]; 33 34 //将临时数组的内容复制到原数组 35 for(int i = l, j = 0; i <= r; i ++, j ++) 36 a[i] = temp[j]; 37 } 38 int main(){ 39 int n; 40 cin >> n; 41 for(int i = 0; i < n; i ++) 42 cin >> a[i]; 43 mergesort(a, 0, n - 1); 44 for(int i = 0; i < n; i ++) 45 cout << a[i] << ' '; 46 cout << endl; 47 return 0; 48 }
下面是一道求逆序对的题目,也就是对归并排序的一个总结。
AC代码
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 5 using namespace std; 6 7 const int N = 500010; 8 int a[N], temp[N], cnt; 9 10 void mergecount(int a[], int l, int r){ 11 if(l == r) 12 return; 13 int mid = l + r >> 1; 14 mergecount(a, l, mid); 15 mergecount(a, mid + 1, r); 16 17 int i = l, j = mid + 1, k = 0; 18 while(i <= mid && j <= r){ 19 // if(a[i] > a[j]){ 20 // cnt += mid - i + 1; 21 // i ++; 22 // }else 23 // j ++; 24 if(a[i] < a[j]){ 25 temp[k ++] = a[i ++]; 26 }else{ 27 cnt += mid - i + 1; 28 temp[k ++] = a[j ++]; 29 } 30 } 31 while(i <= mid) 32 temp[k ++] = a[i ++]; 33 while(j <= r) 34 temp[k ++] = a[j ++]; 35 for(int i = l, j = 0; i <= r; i ++, j ++) 36 a[i] = temp[j]; 37 } 38 int main(){ 39 int n; 40 while(scanf("%d", &n)){ 41 cnt = 0; 42 if(n == 0) 43 break; 44 for(int i = 0; i < n; i ++) 45 scanf("%d", &a[i]); 46 47 mergecount(a, 0, n - 1); 48 49 printf("%d ", cnt); 50 } 51 52 return 0; 53 }