归并排序
2016-12-17 22:06
176 查看
归并排序是一种高效算法,按照分治的三步法,归并排序的三个步骤如下:
划分问题:把序列划分为尽量相等的两半。
递归求解:把两半元素分别排序。
合并问题:把两个有序表合并为一个。
总结为一句话就是,先排列最小的两个相邻的数然后合并两个相邻的序列。
程序流程图如下:
程序实现如下:
下面说明如何判断什么情况下复制左半数组的元素,什么时候复制右半数组的元素:
如果有且仅有一个半数组非空,那么非空数组剩下的数肯定是剩下的数中最小的,因此复制非空数组中的数。当半数组都非空时,选取两个半数组中较小的数,或者相等时任意一个。
由于while循环已经判断至少有一个半数组非空,所以“序列2空”表示“序列1非空序列2空”此时选序列1中的数;由于短路原理,如果执行到“序列2非空,序列1非空,A[p]<=A[q]“说明序列2非空,此时只需判断序列1非空并且序列1中元素小于等于序列2中元素。
拓展部分:
归并排序有一个重要用途,就是统计逆序对。
问题描述:
给一列排序
,求它的逆序对数,即有多少个有序对(i,j),使得i<j但是
。n可以高达1000000.
也许有些读者说可以用一个双重循环算出来,但这样枚举的算法时间渐进复杂度将有O(n2),也就是说该算法实现的程序将会超时。而归并排序的时间渐进复杂度为O(nlogn),此时不失为一个不错的选择。
根据分治三步法:
划分问题:把序列划分为左右两部分
递归求解:求解左右两部分的逆序对
合并问题:统计i在左边但j在右边的逆序对数
问题的关键在于如何合并问题,也就是如何求出i在左边,j在右边的逆序对数目统计的常用方法是分类。对于右边的每个j,把这些逆序对按照j所在位置的不同进行分类,每一个j在左边大于
的元素的数目f(j)即为该分类的数目,所有的f(j)之和便是答案。
在归并排序中,由于左右两边的元素都是排好序的,所以在将某个右边元素复制到T时,此时左边所剩的元素都大于当前右边的元素。所以只需在复制右边元素的时候累加左边所剩元素个数即可统计出逆序对的数目。具体方法是把”else T[i++]=A[q++];”改为”{T[i++]=A[q++];cnt+=m-p;}”。
划分问题:把序列划分为尽量相等的两半。
递归求解:把两半元素分别排序。
合并问题:把两个有序表合并为一个。
总结为一句话就是,先排列最小的两个相邻的数然后合并两个相邻的序列。
程序流程图如下:
程序实现如下:
#include<iostream> using namespace std; void merg_sort(int* A, int x, int y, int* T){//把[x,y)区间内的元素按照从小到大的顺序排好序 if (y - x > 1){ int m = x + (y - x) / 2; int p = x, q = m, i = x; merg_sort(A, x, m, T); merg_sort(A, m, y, T); while (p < m || q < y){ if (q >= y || (p < m&&A[p] <= A[q]))T[i++] = A[p++]; else T[i++] = A[q++]; } for (i = x; i < y; i++)A[i] = T[i]; } } int main() { int A[100], n, T[100]; cin >> n; for (int i = 0; i < n; i++)cin >> A[i]; merg_sort(A, 0, n, T); for (int i = 0; i < n; i++)cout << A[i]<<" "; cout << endl; return 0; }
下面说明如何判断什么情况下复制左半数组的元素,什么时候复制右半数组的元素:
如果有且仅有一个半数组非空,那么非空数组剩下的数肯定是剩下的数中最小的,因此复制非空数组中的数。当半数组都非空时,选取两个半数组中较小的数,或者相等时任意一个。
由于while循环已经判断至少有一个半数组非空,所以“序列2空”表示“序列1非空序列2空”此时选序列1中的数;由于短路原理,如果执行到“序列2非空,序列1非空,A[p]<=A[q]“说明序列2非空,此时只需判断序列1非空并且序列1中元素小于等于序列2中元素。
拓展部分:
归并排序有一个重要用途,就是统计逆序对。
问题描述:
给一列排序
,求它的逆序对数,即有多少个有序对(i,j),使得i<j但是
。n可以高达1000000.
也许有些读者说可以用一个双重循环算出来,但这样枚举的算法时间渐进复杂度将有O(n2),也就是说该算法实现的程序将会超时。而归并排序的时间渐进复杂度为O(nlogn),此时不失为一个不错的选择。
根据分治三步法:
划分问题:把序列划分为左右两部分
递归求解:求解左右两部分的逆序对
合并问题:统计i在左边但j在右边的逆序对数
问题的关键在于如何合并问题,也就是如何求出i在左边,j在右边的逆序对数目统计的常用方法是分类。对于右边的每个j,把这些逆序对按照j所在位置的不同进行分类,每一个j在左边大于
的元素的数目f(j)即为该分类的数目,所有的f(j)之和便是答案。
在归并排序中,由于左右两边的元素都是排好序的,所以在将某个右边元素复制到T时,此时左边所剩的元素都大于当前右边的元素。所以只需在复制右边元素的时候累加左边所剩元素个数即可统计出逆序对的数目。具体方法是把”else T[i++]=A[q++];”改为”{T[i++]=A[q++];cnt+=m-p;}”。
相关文章推荐
- jackson
- 鸟哥私房菜总结
- IntelliJIDEA 15 创建maven项目
- JavaScript闭包
- 详解java类的生命周期
- Mac下配置php7运行环境
- runtime简介
- 2016.12.17 看看当当网
- java多线程(第二种方式)
- NOIP2016 D2T2 蚯蚓
- Simplify Path
- 马尔可夫平稳过程到MCMC采样
- 20155227第二次预备作业
- Bridge 模式
- 排序学习
- 【oracle】plsql连接oracle(localhost连接成功,ip连接提示无监听)问题
- SqueezeNet运用到Faster RCNN进行目标检测+OHEM
- php foreach引用赋值
- CSS3新vw, vh单位
- json-lib 与Jackson性能对比