知识点 归并排序与逆序对

归并排序

大致思想

一种分治法:
定义void Msort(int l, int r)表示将区间 [ l , r ] [l,r] [l,r]排序的过程
1)若 l = = r l == r l==r,则区间只有一个元素,排他干啥2333
2)若 l &lt; r l&lt; r l<r,则区间里有超过一个元素。我们设 m = ( l + r ) d i v 2 m=(l+r)div2 m=(l+r)div2
则我们可以先递归调用

Msort(l, m);
Msort(m + 1,r);

先把前后两个区间 [ l , m ] [l,m] [l,m] [ m + 1 , r ] [m+1,r] [m+1,r]排好序,然后采用序列合并的方法。这样就能够把区间 [ l , r ] [l,r] [l,r]排好序了。

详细代码

例题:P1177 【模板】快速排序

#define USEFASTERREAD 1 

#define rg register
#define inl inline
#define DEBUG printf("[Passing [%s] in line %d.]\n", __func__, __LINE__)
#define putline putchar('\n')
#define putsp putchar(' ')
#define Rep(a, s, t) for(rg int a = s; a <= t; a++)
#define Repdown(a, t, s) for(rg int a = t; a >= s; a--)
typedef long long ll;
#include<cstdio>
#define rs freopen("test.in", "r", stdin), freopen("test.out", "w", stdout)

#if USEFASTERREAD
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
#endif
struct IO
{
	void RS() {rs;} 
	template<typename T> inline IO r(T& x)const
	{
	    x = 0; T f = 1; char ch = getchar();
	    for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
	    for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
	    x *= f; return *this;
	}
	template<typename T> inline IO w(T x)const
	{
	    if(x < 0) {putchar('-'); x = -x;}
	    if(x >= 10) w(x / 10);
	    putchar(x % 10 + '0'); return *this;
	}
	template<typename T> inline IO wl(const T& x)const {w(x), putline; return *this;}
	template<typename T> inline IO ws(const T& x)const {w(x), putsp; return *this;}
	inline IO l() {putline; return *this;}
	inline IO s() {putline; return *this;}
}io;
template<typename T> inline T Max(const T& x, const T& y) {return y < x ? x : y;}
template<typename T> inline T Min(const T& x, const T& y) {return y < x ? y : x;}
template<typename T> inline void Swap(T& x, T& y) {T tmp = x; x = y; y = tmp;}

int N;
int a[100005];
int b[100005];
void Msort(int l, int r)
{
	if(l == r) return;
	int m = (l + r) >> 1;
	Msort(l, m);
	Msort(m + 1, r);
	int i = l, j = m + 1;
	int k = l;
	while(i <= m && j <= r)
	{
		if(a[i] <= a[j]) b[k++] = a[i++];
		else b[k++] = a[j++];
	}
	while(i <= m) b[k++] = a[i++];
	while(j <= r) b[k++] = a[j++];
	for(rg int i = l; i <= r; i++) a[i] = b[i];
}


int main()
{
    //io.RS();
    io.r(N);
    for(rg int i = 1; i <= N; i++) io.r(a[i]);
    Msort(1, N);
    for(rg int i = 1; i <= N; i++) io.ws(a[i]);
    return 0;
}

时空分析

归并排序递归最多 l o g 2 n log_2n log2n次,每次合并的的时间复杂度为 O ( n ) O(n) O(n),故总体时间复杂度稳定在 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)
归并排序所需一个辅助数组,故额外的空间复杂度为 O ( n ) O(n) O(n)

相比较快速排序,归并排序的时间复杂度较稳定,总体表现良好。但我们一般不会使用归并排序,而是使用快速排序,这是因为algorithm头文件中自带sort()函数,它采用一种神奇的方式进行排序,出题人再毒瘤也完全没有办法卡掉它。另外,归并排序比快速

稳定性

归并排序是一种稳定的排序,也就是说若两个元素 a i = = a j a_i==a_j ai==aj,且在原序列中 a i a_i ai a j a_j aj之前,那么排完序后, a i a_i ai仍然会在 a j a_j aj之前。这就是归并排序的稳定性。这是一种不错的性质。而快速排序是一种非稳定的排序,没有这种性质。
如果我们需要使用一个稳定的排序算法,显然我们不能使用algorithm里的sort()。那怎么办?手打一个归并?NONONO! 我们可以使用algorithm里的stable_sort()函数,它是一个稳定的排序算法,大致用法与sort()一般。
STL真是个神奇的东西2333。

逆序对

定义

设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。
如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数。——百度百科

枚举法

也就是说,我们要找所有满足 1 ≤ i &lt; j ≤ n   a n d   A [ i ] &gt; A [ j ] 1 ≤ i &lt; j ≤ n\ and\ A[i] &gt; A[j] 1i<jn and A[i]>A[j]这样的 ( i , j ) (i,j) (i,j)的对数。
一种显而易见的方法是枚举法,时间复杂度 O ( n 2 ) O(n^2) O(n2)
对于这道例题,我们显然1s内跑不出来。我们需要一种更高效的方法

归并排序法

还记得我们之前讲了啥吗?归并排序,对。我们在上文的代码中注意到这样一句

	while(i <= m && j <= r)
	{
		if(a[i] <= a[j]) b[k++] = a[i++];
		else b[k++] = a[j++];
	}

显然我们可知在代码中, i &lt; j i&lt;j i<j
1)考虑第三行的情况: a [ i ] ≤ a [ j ] a[i]\leq a[j] a[i]a[j],这样是顺序的,没有产生逆序对。
2)考虑第四行的情况: a [ i ] &gt; a [ j ] a[i]&gt;a[j] a[i]>a[j],这样是逆序的。
不仅仅 a [ i ] a[i] a[i] a [ j ] a[j] a[j]是逆序的,由于区间 [ l , m ] [l, m] [l,m]已经在之前排好了序,所以所有 a [ k ] ( i ≤ k ≤ m ) a[k](i\leq k\leq m) a[k](ikm) &gt; a [ i ] &gt;a[i] >a[i],故在 a [ j ] a[j] a[j]处会产生 m − i + 1 m-i+1 mi+1个逆序对。在排序的同时把对答案的贡献加上即可。

详细代码

#define USEFASTERREAD 1 

#define rg register
#define inl inline
#define DEBUG printf("[Passing [%s] in line %d.]\n", __func__, __LINE__)
#define putline putchar('\n')
#define putsp putchar(' ')
#define Rep(a, s, t) for(rg int a = s; a <= t; a++)
#define Repdown(a, t, s) for(rg int a = t; a >= s; a--)
typedef long long ll;
#include<cstdio>
#define rs freopen("test.in", "r", stdin), freopen("test.out", "w", stdout)

#if USEFASTERREAD
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
#endif
struct IO
{
	void RS() {rs;} 
	template<typename T> inline IO r(T& x)const
	{
	    x = 0; T f = 1; char ch = getchar();
	    for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
	    for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
	    x *= f; return *this;
	}
	template<typename T> inline IO w(T x)const
	{
	    if(x < 0) {putchar('-'); x = -x;}
	    if(x >= 10) w(x / 10);
	    putchar(x % 10 + '0'); return *this;
	}
	template<typename T> inline IO wl(const T& x)const {w(x), putline; return *this;}
	template<typename T> inline IO ws(const T& x)const {w(x), putsp; return *this;}
	inline IO l() {putline; return *this;}
	inline IO s() {putline; return *this;}
}io;
template<typename T> inline T Max(const T& x, const T& y) {return y < x ? x : y;}
template<typename T> inline T Min(const T& x, const T& y) {return y < x ? y : x;}
template<typename T> inline void Swap(T& x, T& y) {T tmp = x; x = y; y = tmp;}

int N;
int a[500005];
int b[500005];
ll ans;
void Msort(int l, int r)
{
	if(l == r) return;
	int m = (l + r) >> 1;
	Msort(l, m);
	Msort(m + 1, r);
	int i = l, j = m + 1;
	int k = l;
	while(i <= m && j <= r)
	{
		if(a[i] <= a[j]) b[k++] = a[i++];
		else
		{
			ans += m - i + 1;
			b[k++] = a[j++];
		}
	}
	while(i <= m) b[k++] = a[i++];
	while(j <= r) b[k++] = a[j++];
	for(rg int i = l; i <= r; i++) a[i] = b[i];
}


int main()
{
    //io.RS();
    io.r(N);
    for(rg int i = 1; i <= N; i++) io.r(a[i]);
    Msort(1, N);
    io.wl(ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日居月诸Rijuyuezhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值