前言
学了这么久的归并排序,感觉没什么卵用。。。。而且STL中有sort这么强大的工具,归并排序早就吃灰了
先回顾下什么是归并排序
要想知道什么是归并排序,先看看什么是归并
举个例子:将两个已经排好序(例如从小到大)的数组合并成一个有序数组,要求时间复杂度O(n)
用two-pointer的思想很好解决,用一个pointer指向第一个数组,另一个指向第二个数组,比较两个pointer所指的元素大小,哪个小九八哪个放进结果数组中,并把刚刚的pointer往后移,,,,,很简单就不再说了,代码如下
基于这种想法,可以得到归并排序的算法
一开始数组是无序的,当分割的两个数组都只有一个元素时,即可排序。此时两个元素的数组就有序,接着四个元素的数组就有序。。。。。依次,可以得到最终的有序数组,显然这种排序算法的时间复杂度为O(logN)
Coding如下:
const int MAX = 5e5+5;
int N;
int a[MAX];
void merge(int l1,int r1,int l2,int r2) {
int i=l1,j=l2;
int tmp[MAX],count=0;
while (i<=r1&&j<=r2) {
if(a[i]<=a[j]) tmp[count++]=a[i++];
else {
tmp[count++]=a[j++];
res += r1-i+1;
}
}
while (i<=r1) tmp[count++]=a[i++];
while (j<=r2) tmp[count++]=a[j++];
for (int k = 0; k < count; ++k) {
a[l1+k]=tmp[k];
}
}
void mergeSort(int l,int r){
if(l>=r) return;
int mid = (l+r)/2;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,mid+1,r);
}
下面看应用,题目如下
题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai>aj 且 i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
Update:数据已加强。
输入格式
第一行,一个数 n,表示序列中有 n个数。
第二行 n 个数,表示给定的序列。序列中每个数字不超过 10^9。
输出格式
输出序列中逆序对的数目。
输入输出样例
输入 #1复制
6
5 4 2 6 3 1
输出 #1复制
11
说明/提示
对于 25% 的数据,n≤2500
对于50% 的数据,n≤4×10^4。
对于所有数据,n≤5×10^5
分析
如果直接使用二重循环,显然数据太大,一定会超时!!!!
归并排序思想的应用:
拿样例来说,最后一步归并时元素如下
2 4 5 | 1 3 6
可以看出,逆序对的个数可以有如下规律:对于右边的每一个元素,在左边找到第一个比右边大的元素的下标,每个右边的元素可以产生
3-左边元素的下标(从1开始)+1 个逆序对。由此可以写出如下代码
const int MAX = 5e5+5;
int N;
int a[MAX];
ll res;
void merge(int l1,int r1,int l2,int r2) {
int i=l1,j=l2;
int tmp[MAX],count=0;
while (i<=r1&&j<=r2) {
if(a[i]<=a[j]) tmp[count++]=a[i++];
else {
tmp[count++]=a[j++];
res += r1-i+1; //只会在这里产生逆序对
}
}
while (i<=r1) tmp[count++]=a[i++];
while (j<=r2) tmp[count++]=a[j++];
for (int k = 0; k < count; ++k) {
a[l1+k]=tmp[k];
}
}
void mergeSort(int l,int r){
if(l>=r) return;
int mid = (l+r)/2;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,mid+1,r);
}
加强版逆序对
题目链接:P1966火柴排队
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 5e5+5;
const int INF = 0x7fffffff;
const int MOD = 1e8-3;
int N,K;
int a[MAX];
ll res;
struct Point {
int preValue,index;
}pa[MAX],pb[MAX];
void merge(int l1,int r1,int l2,int r2) {
int i=l1,j=l2;
int tmp[MAX],count=0;
while (i<=r1&&j<=r2) {
if(a[i]<=a[j]) tmp[count++]=a[i++];
else {
tmp[count++]=a[j++];
res = (res+r1-i+1)%MOD;
}
}
while (i<=r1) tmp[count++]=a[i++];
while (j<=r2) tmp[count++]=a[j++];
for (int k = 0; k < count; ++k) {
a[l1+k]=tmp[k];
}
}
void mergeSort(int l,int r){
if(l>=r) return;
int mid = (l+r)/2;
mergeSort(l,mid);
mergeSort(mid+1,r);
merge(l,mid,mid+1,r);
}
bool cmp(const Point& aa,const Point& bb) {
return aa.preValue<bb.preValue;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#ifdef LOCAL
freopen("../data", "r", stdin);
#endif
cin >> N;
for (int i = 0; i < N; ++i) {
cin >> pa[i].preValue;
pa[i].index=i;
}
for (int i = 0; i < N; ++i) {
cin >> pb[i].preValue;
pb[i].index=i;
}
sort(pa,pa+N,cmp);
sort(pb,pb+N,cmp);
mergeSort(0,N-1);
for (int i = 0; i < N; ++i) {
a[pa[i].index]=pb[i].index;
}
mergeSort(0,N-1);
cout << res << endl;
}