归并排序
大致思想
一种分治法:
定义void Msort(int l, int r)
表示将区间
[
l
,
r
]
[l,r]
[l,r]排序的过程
1)若
l
=
=
r
l == r
l==r,则区间只有一个元素,排他干啥2333
2)若
l
<
r
l< 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]排好序了。
详细代码
#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
<
j
≤
n
a
n
d
A
[
i
]
>
A
[
j
]
1 ≤ i < j ≤ n\ and\ A[i] > A[j]
1≤i<j≤n 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
<
j
i<j
i<j
1)考虑第三行的情况:
a
[
i
]
≤
a
[
j
]
a[i]\leq a[j]
a[i]≤a[j],这样是顺序的,没有产生逆序对。
2)考虑第四行的情况:
a
[
i
]
>
a
[
j
]
a[i]>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](i≤k≤m)都
>
a
[
i
]
>a[i]
>a[i],故在
a
[
j
]
a[j]
a[j]处会产生
m
−
i
+
1
m-i+1
m−i+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;
}