归并排序之求逆序对
何谓归并排序,先看下面一个例子:
设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1};
第二次归并后:{6,100,202,301},{1,8,38};
第三次归并后:{1,6,8,38,100,202,301};
最终结果得出{1,6,8,38,100,202,301}
归并排序是一种稳定且高效的排序算法,时间复杂度为O(nlog2n) ,一般按照三步走:
1.划分问题:把序列分成元素个数尽量相等的两半
2.递归求解:把两半元素分别排序
3.合并问题:把两个有序表合并为一个
前两步很容易实现,关键在于第三步,怎么合并呢? 很简单,每次只需把左右两边序列的最小元素进行比较,把其中较小的元素加入到合并后的辅助数组中即可。示例代码如下:
//归并排序
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N] ,b[N];//b为辅助数组
void merge_sort(int l , int r)
{
if(r-l > 0)//如果整个区间中元素个数大于1,则继续分割
{
int mid = (l+r) / 2 ;
int i = l; //辅助数组的下标
int p = l , q = mid+1;
merge_sort(l , mid);
merge_sort(mid+1 , r);
//printf("%d-%d %d-%d\n",p,mid ,q ,r);
while(p<=mid || q<=r)//左右两部分只要有一部分不为空
{
if(q>r || (p<=mid && a[p]<=a[q]))//从左半数组复制到辅助数组
b[i++] = a[p++];
else b[i++] = a[q++];//从右半数组复制到辅助数组
}
for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中
a[i] = b[i];
}
}
int main()
{
int n;
while(cin >> n)
{
for(int i = 1 ; i <= n; i ++)
cin >> a[i];
merge_sort(1 , n);
for(int i = 1; i <= n; i++)
cout << a[i] << " ";
cout << endl;
}
return 0;
}
求序列的逆序对,先看下面的例子:
设有数列{6,202,100,301,38,8,1}
初始状态:6,202,100,301,38,8,1
第一次归并后:{6,202},{100,301},{8,38},{1},比较次数:3;
第二次归并后:{6,100,202,301},{1,8,38},比较次数:4;
第三次归并后:{1,6,8,38,100,202,301},比较次数:4;
总的比较次数为:3+4+4=11;
逆序数为14;
根据归并排序的特性(左右两部分的有序序列合并时,假设i在左边,j在右边,对于右边的j,统计左边比它大的元素个数f(j),则f(j) = mid-i+1 ,合并万所有的序列时即可得出答案,即f(j)之和便是答案),只需将上面的代码修改一处:把“else b[i++] = a[q++];”改成“ else {b[i++] = a[q++]; cnt += mid-p+1;}" ,注意在调用之前将cnt清零。
//归并排序及求逆序对
#include<bits/stdc++.h>
using namespace std;
#define N 1000005
int a[N] ,b[N];//b为辅助数组
long long cnt;
void merge_sort(int l , int r)
{
if(r-l > 0)//如果整个区间中元素个数大于1,则继续分割
{
int mid = (l+r) / 2 ;
int i = l; //辅助数组的下标
int p = l , q = mid+1;
merge_sort(l , mid);
merge_sort(mid+1 , r);
//printf("%d-%d %d-%d\n",p,mid ,q ,r);
while(p<=mid || q<=r)//左右两部分只要有一部分不为空
{
if(q>r || (p<=mid && a[p]<=a[q]))//从左半数组复制到辅助数组
b[i++] = a[p++];
else
{
b[i++] = a[q++];
cnt += mid -p +1;//将逆序对的个数累加起来
}
}
for(i = l ; i <= r; i++)//将b中排好序的元素复制到a中
a[i] = b[i];
}
}
int main()
{
int n;
while(cin >> n)
{
for(int i = 1 ; i <= n; i ++)
cin >> a[i];
cnt = 0;
merge_sort(1 , n);
for(int i = 1; i <= n; i++)
cout << a[i] << " ";
cout << endl;
cout << "逆序对有:" << cnt <<endl;
}
return 0;
}
当然求逆序对还可以用树状数组求,可以百度一波
上一篇: 前端零碎知识点补充--1
下一篇: 大厂前端经典面试问题精选(附答案)