归并排序
来源:互联网 发布:数据库数据如何查找 编辑:程序博客网 时间:2024/06/05 11:21
1、引例
在说明归并排序之前,我们先看看这样的一个问题:
给出两个序列Ln1,Rn2(Ln1,Rn2都为非递减序列)。现在问题是,让你将Ln1和Rn2合并为一个非递减序列。
我们稍微一分析,容易写出下列代码:
void merge(int *L, int n1, int *R, int n2, int *arr){ int i = 0, j = 0, k = 0; while(i < n1 && j < n2){ if(L[i] < R[i]) arr[k++] = L[i++]; else arr[k++] = R[j++]; } while(i < n1) arr[k++] = L[i++]; while(j < n2) arr[k++] = R[j++];}
2、归并排序
但是现在我们把问题改成:
给定一无序序列an(n为序列长度),现在让你将该序列变为非递减的序列
显然,现在我们的问题就变为一个排序问题.通过上述的例子,我们不禁会联想到一种排序方法.如果序列an的[0, n/2)与[n/2, n)都排好序了,那么我们直接使用刚才例子的方法合并就可以了.那么接着我们就会思考,一个序列什么时候他的前一半和后一半都是排好序的.稍微一思考我们容易想到当序列长度为2的时候,其前一半和后一半自然就是有序的(因为他的前一半和后一半都各自只有一个元素).那么我们很自然的可以想到,把一个序列不断的分割直到他的长度为1时,我们就开始合并.于是我们就可以得出如下代码:
void merge(int *arr, int start, int mid, int end){ //额外申请空间保存区间[start, mid)与[mid, end)的数 int n1 = mid - start; int n2 = end - mid; int *L = new int[n1]; int *R = new int[n2]; int i = 0, j = 0, k = start; while(i < n1) L[i++] = arr[k++]; while(j < n2) R[j++] = arr[k++]; //将Ln1与Rn2合并 i = j = 0, k = start; while(i < n1 && j < n2){ if(L[i] < R[j]) arr[k++] = L[i++]; else arr[k++] = R[j++]; } while(i < n1) arr[k++] = L[i++]; while(j < n2) arr[k++] = R[j++]; //释放Ln1,Rn2的空间 delete[] L; delete[] R; L = R = nullptr;}void merge_sort(int *arr, int start, int end){ if(start+1 < end){ //区间至少有一个元素 int mid = (start + end) >> 1; //区间中点,将区间一分为二 merge_sort(arr, start, mid); //对区间[start, mid)进行排序 merge_sort(arr, mid, end); //对区间[mid, end)进行排序 merge(arr, start, mid, end); //合并区间[start, mid)和[mid, end) }}
这样我们就实现了归并排序。那么我们来分析下其复杂度。首先时间复杂度,我们容易看出它的时间由递归的深度和每一层递归的宽度。由于每次一分为二,递归深度为logn,每一层都有n个元素,其宽度为n。故其时间复杂度为O(nlogn)。我们分析下他的空间复杂度,由于每次归并我们需要额外的空间来保存数据,故可得到归并排序的空间复杂度为O(n)。
3、归并排序求逆序对数
假设a[1...n]是有n个数的序列。若i < j且A[i] > A[j],则对偶(i, j)称为A的逆序对。现在问题是给定一个序列an,问其有多少对逆序对?
看到这个问题我们可以想到,归并排序时,将序列an一分为二Ln1, Rn2。在非降序的排序中,如果L[i] <= R[j],显然没有逆序数,只有当L[i] > R[j]时产生逆序数。此时L[i+1...n1]里的元素均比R[j]大,而R[j]又在它们的后面.所以,此时的逆序对数:n1-i。
我们来一道题练练手:POJ1804 Brainman。附上该题代码:
#include <cstdio>#define MAXN (1000)int inversion, a[MAXN];void merge(int *arr, int start, int mid, int end){ int n1 = mid - start; int n2 = end - mid; int *L = new int[n1]; int *R = new int[n2]; int i = 0, j = 0, k = start; while(i < n1) L[i++] = arr[k++]; while(j < n2) R[j++] = arr[k++]; i = j = 0, k = start; while(i < n1 && j < n2){ if(L[i] > R[j]) arr[k++] = R[j++], inversion += n1 - i;//加上逆序对 else arr[k++] = L[i++]; } while(i < n1) arr[k++] = L[i++]; while(j < n2) arr[k++] = R[j++]; delete[] L; delete[] R; L = R = NULL;}void merge_sort(int *arr, int start, int end){ if(start+1 < end){ int mid = (start + end) >> 1; merge_sort(arr, start, mid); merge_sort(arr, mid, end); merge(arr, start, mid, end); }}int main(){ int T, n; scanf("%d", &T); for(int nCase = 1; nCase <= T; nCase++){ scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%d", &a[i]); inversion = 0; merge_sort(a, 0, n); printf("Scenario #%d:\n%d\n\n", nCase, inversion); } return 0;}
1 0
- 归并排序-归并排序
- 归并和归并排序
- 归并与归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 排序::归并
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- Linux 游戏服务器搭建教程详解
- uml类图中关联与泛化的区别
- linux用户登录日志
- Rotate Array
- Spring的自动装配(byName;byType)
- 归并排序
- LA 3602 DNA 序列 模拟
- tomcat服务器分包
- 字符串转数字
- 使用 Android Studio 进行 NDK 开发
- 线程安全的单例模式
- ZOJ 3870 本篇文章转载自——白羊_baiyang
- 免费的论文查重网站
- Python使用traceback.print_exc()来代替print e 来输出详细的异常信息