6、逆序对问题
排列的逆序数是指在一个排列中,若两个元素的先后顺序与排序后它们的顺序相反,则称这一对元素为一个逆序。排列的逆序数就是这个排列中逆序对的个数。
求排列的逆序数有多种方法,包含暴力枚举、归并排序、使用树状数组(BIT)、线段树(Segment Tree) 等方法。
1、暴力枚举
使用暴力法求解逆序对的C++代码示例:
C++ 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 #include <iostream>
using namespace std ;
int countInversePairs ( int arr [], int n ) {
int count = 0 ;
for ( int i = 0 ; i < n -1 ; i ++ ) {
for ( int j = i + 1 ; j < n ; j ++ ) {
if ( arr [ i ] > arr [ j ]) {
count ++ ;
}
}
}
return count ;
}
int main () {
int arr [] = { 2 , 4 , 1 , 3 , 5 };
int n = sizeof ( arr ) / sizeof ( arr [ 0 ]);
int inversePairs = countInversePairs ( arr , n );
cout << "Number of inverse pairs: " << inversePairs << endl ;
return 0 ;
}
这段代码中,countInversePairs
函数使用嵌套循环遍历数组,对于每对元素 (arr[i], arr[j]),如果arr[i] > arr[j],则逆序对的计数增加。然后,在main
函数中,创建一个示例数组,使用countInversePairs
函数计算逆序对的数量,并将结果打印出来。
2、归并排序
通过归并排序来统计逆序对的个数。具体步骤如下:
将排列分成左右两个部分;
递归地求解左右两部分的逆序数;
合并左右两部分,并统计由左部分中的元素和右部分中的元素构成的逆序对;
返回左右两部分逆序对的个数和合并后的逆序对个数之和。
下面是使用C++代码实现求排列的逆序数的函数:
C++ 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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 #include <iostream>
#include <vector>
using namespace std ;
// 归并函数
int merge ( vector < int >& nums , int left , int mid , int right ) {
vector < int > temp ( right - left + 1 );
int i = left ; // 左序列指针
int j = mid + 1 ; // 右序列指针
int k = 0 ; // 临时数组指针
int count = 0 ; // 记录逆序对个数
while ( i <= mid && j <= right ) {
if ( nums [ i ] <= nums [ j ]) {
temp [ k ++ ] = nums [ i ++ ];
} else {
// 如果左序列元素大于右序列元素
// 则左序列后面的元素都大于右序列当前元素
// 因此逆序对个数要加上左序列剩余元素的个数
count += mid - i + 1 ;
temp [ k ++ ] = nums [ j ++ ];
}
}
// 将左序列剩余元素添加到临时数组中
while ( i <= mid ) {
temp [ k ++ ] = nums [ i ++ ];
}
// 将右序列剩余元素添加到临时数组中
while ( j <= right ) {
temp [ k ++ ] = nums [ j ++ ];
}
// 将临时数组中的元素复制回原数组
for ( int m = 0 ; m < temp . size (); m ++ ) {
nums [ left + m ] = temp [ m ];
}
return count ;
}
// 归并排序函数
int mergeSort ( vector < int >& nums , int left , int right ) {
int count = 0 ;
if ( left < right ) {
int mid = ( left + right ) / 2 ;
// 分治递归
count += mergeSort ( nums , left , mid );
count += mergeSort ( nums , mid + 1 , right );
// 合并子序列并计算逆序对个数
count += merge ( nums , left , mid , right );
}
return count ;
}
// 统计逆序对个数函数
int countInversePairs ( vector < int >& nums ) {
int n = nums . size ();
return mergeSort ( nums , 0 , n - 1 );
}
int main () {
vector < int > nums = { 7 , 5 , 6 , 4 };
int inversePairs = countInversePairs ( nums );
cout << "逆序对个数为:" << inversePairs << endl ;
return 0 ;
}
通过调用 countInversePairs()
函数,可以统计给定数组中的逆序对个数。以上是一个简单的示例,你可以根据需要进行修改和扩展。希望对你有帮助!如果你有任何问题,请随时问我。
3、树状数组(BIT)
使用树状数组求解逆序对的C++代码示例:
C++ 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 #include <iostream>
#include <vector>
using namespace std ;
// 树状数组的更新操作,将指定位置的值加上给定的增量
void update ( vector < int >& tree , int index , int val ) {
while ( index < tree . size ()) {
tree [ index ] += val ;
index += index & - index ; // 更新下一个需要加上增量的位置
}
}
// 树状数组的查询操作,计算从1到指定位置的累加和
int query ( vector < int >& tree , int index ) {
int sum = 0 ;
while ( index > 0 ) {
sum += tree [ index ];
index -= index & - index ; // 更新下一个需要查询的位置
}
return sum ;
}
// 使用树状数组统计逆序对的个数
int countInversions ( vector < int >& nums ) {
int n = nums . size ();
vector < int > tree ( n + 1 , 0 ); // 初始化树状数组
int inversions = 0 ;
for ( int i = n - 1 ; i >= 0 ; i -- ) {
inversions += query ( tree , nums [ i ] - 1 ); // 统计小于当前数的数的个数
update ( tree , nums [ i ], 1 ); // 更新树状数组
}
return inversions ;
}
int main () {
vector < int > nums = { 5 , 2 , 6 , 1 };
int inversions = countInversions ( nums );
cout << "逆序对的个数为:" << inversions << endl ;
return 0 ;
}
这段代码使用树状数组实现了统计逆序对的功能。首先定义了树状数组的更新和查询函数,分别用于更新树状数组的值和计算前缀和。然后在 countInversions
函数中,从数组的末尾开始遍历,使用树状数组来统计小于当前数的数的个数,并对树状数组进行更新。最后返回得到的逆序对个数。
在 main
函数中,我们定义了一个示例数组 [5, 2, 6, 1]
,并调用 countInversions
函数来统计逆序对的个数。最后输出结果。
4、线段树(Segment Tree)
使用线段树来求解逆序对的C++代码示例:
C++ 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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 #include <iostream>
#include <vector>
using namespace std ;
// 线段树节点
class Node {
public :
int start ;
int end ;
int count ;
Node * left ;
Node * right ;
Node ( int start , int end ) {
this -> start = start ;
this -> end = end ;
this -> count = 0 ;
this -> left = nullptr ;
this -> right = nullptr ;
}
};
// 更新线段树中的节点
void update ( Node * root , int index ) {
if ( root == nullptr )
return ;
if ( root -> start == root -> end ) {
root -> count ++ ;
return ;
}
int mid = ( root -> start + root -> end ) / 2 ;
if ( index <= mid ) {
if ( root -> left == nullptr )
root -> left = new Node ( root -> start , mid );
update ( root -> left , index );
}
else {
if ( root -> right == nullptr )
root -> right = new Node ( mid + 1 , root -> end );
update ( root -> right , index );
}
// 更新当前节点的逆序对数量
root -> count ++ ;
if ( root -> left != nullptr )
root -> count += root -> left -> count ;
if ( root -> right != nullptr )
root -> count += root -> right -> count ;
}
// 查询线段树中小于等于index的逆序对数量
int query ( Node * root , int index ) {
if ( root == nullptr || index < root -> start )
return 0 ;
if ( index >= root -> end )
return root -> count ;
int mid = ( root -> start + root -> end ) / 2 ;
int result = query ( root -> left , index );
if ( index > mid && root -> right != nullptr )
result += query ( root -> right , index );
return result ;
}
int countInversePairs ( vector < int >& nums ) {
int n = nums . size ();
int count = 0 ;
if ( n < 2 )
return count ;
int maxNum = INT_MIN ;
int minNum = INT_MAX ;
for ( int i = 0 ; i < n ; i ++ ) {
maxNum = max ( maxNum , nums [ i ]);
minNum = min ( minNum , nums [ i ]);
}
Node * root = new Node ( minNum , maxNum );
for ( int i = n -1 ; i >= 0 ; i -- ) {
count += query ( root , nums [ i ] - 1 );
update ( root , nums [ i ]);
}
return count ;
}
int main () {
vector < int > nums = { 2 , 4 , 1 , 3 , 5 };
int inversePairs = countInversePairs ( nums );
cout << "Number of inverse pairs: " << inversePairs << endl ;
return 0 ;
}
这段代码中,首先定义了一个线段树节点类 Node
,表示线段树的节点,包含了节点的起始位置、结束位置、逆序对数量、左子节点和右子节点。然后,在 update
函数中,通过递归的方式构建线段树,并在更新节点时记录逆序对的数量。在 query
函数中,通过递归的方式查询小于等于给定位置的逆序对数量。
在 countInversePairs
函数中,先找到数组中的最大值和最小值,然后创建根节点,并依次遍历数组中的元素,在遍历的过程中更新线段树和计算逆序对的数量。
最后,在 main
函数中,创建一个示例数组,使用 countInversePairs
函数计算逆序对的数量,并将结果打印出来。
本页面的全部内容在 小熊老师 - 莆田青少年编程俱乐部 0594codes.cn 协议之条款下提供,附加条款亦可能应用