逆序数

在数列 中,如果一组数(i, j)满足 且 i < j,
那么这组数就称为一个逆序,数列A中的逆序的数量称为逆序数.逆序数与在该数列上执行冒泡排序交换的次数相等.
求给定数列A的逆序数,如果直接使用冒泡排序会导致 Time Limit Exceeded.

分析

  由于采用冒泡排序计算逆序数在大数组上会导致超时,无法在限定时间内算出正确结果,故采用分治策略,在分支策略中,
选择使用归并排序.

  根据分治策略,将数列划分为左右两个子数列 L, R 后,该数列的逆序数 = L的逆序数 + R的逆序数 + 合并时两数组时产生的逆序数,由上可得如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
long count_inversions(vector<int>& data, int low, int high)
{
long v1, v2, v3;
int mid = (low + high) / 2
if (low + 1 < high)
{
v1 = count_inversions(data, low, mid);
v2 = count_inversions(data, mid, high);
v3 = merge_sort(data, low, mid, high);
return v1 + v2 + v3;
}
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
llong count_inversions(vector<int>& data, int low, int high)
{
llong v1, v2, v3;
int mid = (low + high) / 2
if (low + 1 < high)
{
v1 = count_inversions(data, low, mid);
v2 = count_inversions(data, mid, high);
v3 = merge_sort(data, low, mid, high);
return v1 + v2 + v3;
}
return 0;
}

  两数列合并时的逆序数应该等于 L 中的元素移动到 R 的各个元素之后的数量之和,R 每个元素单独计算.
设L长度为 n1, L 当前下标为 i , R 当前下标为 j ,只要在合并R各元素时计算 n1 - i, 就能知道L中多少个元素移动到 R[j] 后方,
即当前有多少个与 R[j] 相关的逆序数.由上可得如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
llong merge_sort(vector<int>& a, int low, int mid, int high)
{
int i = 0, j = 0, k;
llong cnt = 0;
vector<int> left(a.begin() + low, a.begin() + mid);
vector<int> right(a.begin() + mid, a.begin() + high);
for (k = low; k < high and i < left.size() and j < right.size(); ++k)
{
if (left[i] <= right[j])
{
a[k] = left[i];
++i;
}else
{
a[k] = right[j];
++j;
cnt += left.size() - i;
}
}
while (i < left.size())
{
a[k] = left[i];
++k;
++i;
}
while (j < right.size())
{
a[k] = right[j];
++k;
++j;
}
return cnt;
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
llong merge_sort(vector<int>& a, int low, int mid, int high)
{
int i = 0, j = 0, k;
llong cnt = 0;
vector<int> left(a.begin() + low, a.begin() + mid);
vector<int> right(a.begin() + mid, a.begin() + high);
for (k = low; k < high and i < left.size() and j < right.size(); ++k)
{
if (left[i] <= right[j])
{
a[k] = left[i];
++i;
}else
{
a[k] = right[j];
++j;
cnt += left.size() - i;
}
}
while (i < left.size())
{
a[k] = left[i];
++k;
++i;
}
while (j < right.size())
{
a[k] = right[j];
++k;
++j;
}
return cnt;
}

llong count_inversions(vector<int>& data, int low, int high)
{
llong v1, v2, v3;
int mid = (low + high) / 2;
if (low + 1 < high)
{
v1 = count_inversions(data, low, mid);
v2 = count_inversions(data, mid, high);
v3 = merge_sort(data, low, mid, high);
return v1 + v2 + v3;
}
return 0;
}