1. 逆序对
假设 A 为一个有 n 个数字的序列 (n > 1),其中所有数字可以完全不相同,也可以存在相同。如果存在正整数 i, j 使得 0 ≤ i < j ≤ n - 1 而且 A[i] > A[j]
,则 (A[i], A[j])
这个有序对称为 A 的一个逆序对,也称作逆序数。比如:
输入的序列为: 5 1 1 4 2 3
逆序对有: (5,1)、 (5,1)、(5,4)、(5,2)、(5,3)、(4,2)、(4,3)
2. 计算逆序对
逆序对数目发求法主要有以下两种:暴力法和归并法。下面就详细说下这两种方法的解题思想以及具体的实现代码。
2.1 暴力法
(1)暴力法的思想是通过两重循环来枚举所有的有序对,然后求出逆序对的数目。因此,暴力法的时间复杂度为O(N2),空间复杂度为O(1)。
(2)暴力法的Python代码实现如下:1
2
3
4
5
6
7
8
9def count_reversepair(nums):
# nums为输入的数字序列
count = 0
for i in range(len(nums)):
# 第二重循环注意是从 i+1 开始
for j in range(i+1, len(nums)):
if nums[i] > nums[j]:
count += 1
return count
2.2 归并法
(1)归并法则是利用归并排序的思想来求解逆序对的数目,归并排序的详细过程就不具体说了,简言之就是:先将原序列拆分为左、右子序列,直至不可再拆分,然后将左、右子序列进行有序合并,直到整个序列都合并完。而计算逆序对的过程,就在有序合并这个步骤中,比如上面的例子 nums=[5, 1, 1, 4, 2, 3]
:
a. 在最后一次有序合并步骤中(注意前面的已经排好序了),左子序列为left_nums=[1, 1, 5]
,右子序列为right_nums=[2, 3, 4]
;
b. 然后设置left_begin
、left_end
为left_nums
的开始位置和结束位置,设置right_begin
、right_end
为right_nums
的开始位置和结束位置;
c. 然后循环遍历left_nums
和right_nums
:
· 如果left_nums[left_begin] > right_nums[right_begin]
,说明左边最小值大于右边最小值,即左边所有值都能与右边最小值构成逆序对,此时left_begin
往右遍历;
· 否则right_begin
往右遍历,继续比较left_nums[left_begin]
和right_nums[right_begin]
,直到某一边遍历完结束。
归并法利用了归并排序的思想,故其时间复杂度为O(NlogN),空间复杂度为O(N)。
(2)归并法的Python代码实现如下: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
50def merge(nums, left_begin, left_end, right_begin, right_end, count):
# 最后拷贝回 nums 的起始位置
nums_begin = left_begin
# 用来存储排序后的结果
sorted_list = [0] * (right_end - left_begin + 1)
i = 0
while left_begin <= left_end and right_begin <= right_end:
# 发现逆序对,如果左边最小值大于右边最小值,则左边所有值都能构成逆序对
if nums[left_begin] > nums[right_begin]:
count += (left_end - left_begin + 1)
sorted_list[i] = nums[right_begin]
right_begin += 1
else:
sorted_list[i] = nums[left_begin]
left_begin += 1
i += 1
# 左边有剩余未参与比较的
while left_begin <= left_end:
sorted_list[i] = nums[left_begin]
left_begin += 1
i += 1
# 右边有剩余未参与比较的
while right_begin <= right_end:
sorted_list[i] = nums[right_begin]
right_begin += 1
i += 1
# 最后将 sorted_list 拷贝回 nums 中
for num in sorted_list:
nums[nums_begin] = num
nums_begin += 1
# 归并思想求逆序对
def merge_sort(nums, begin, end, count):
if begin >= end:
return
mid = (begin + end) // 2
# 拆分左、右子序列
merge_sort(nums, begin, mid, count)
merge_sort(nums, mid+1, end, count)
# 有序合并
merge(nums, begin, mid, mid+1, end, count)
##########################################################
# nums = [5, 1, 1, 4, 2, 3]
# cnt = 0
# merge_sort(nums, 0, len(nums) - 1, cnt)
# print(cnt) # cnt 就是逆序对数目
##########################################################

...
...