剑指 Offer 51. 数组中的逆序对
题目链接-来源:力扣(LeetCode)
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
思路:
有一个基础知识:排序算法的本质就是不停寻找数组中的逆序对,然后一个个交换消灭之。那么我们只要用一个复杂度较低 O(nlong) 且每一步排序交换一组逆序对的排序算法为载体,每次交换时计数一次逆序对,最终返回值即是我们需要的逆序对的总数。
这里自然想到用「归并排序」为载体(注意,O(nlogn)复杂度的排序方法很多,堆排序每次维护最小堆时的交换步骤也可用来计数;快速排序由于其无序性,每次划分可能会一次性消灭很多对逆序对,不方便统计)。
现在我们仔细思考,每次「交换」操作,跟逆序对的具体数量关系如何。首先,某一轮归并排序的输入是两个有序的数组,记为 A[],B[]。指针 pA 指向 A[] 开头, pB 指向 B[] 开头。当我们进行若干次归并以后,pA = i,pB = j。
假设 A[i] <= B[j] ,则我们要添加 A[i] 进入归并序列,同时 pA++。考察关于 A[i] 的逆序对数量,我们可以有如下的信息和推论:
- 我们默认 A[] 的所有元素目前都在 B[] 的「左侧」(也就是完成排序后较小元素所在的那一侧)
- 在 A[] 和 B[] 内部逆序对的数量为0(两个数组已经分别有序),也就是对于 A[i],我们不需要考虑 A[] 中任何元素,只需要考虑 B[]
- 我们的取数策略(从小到大取数),导致:我们当前取到的元素(A[i])大于等于任何已取到的元素(A[i - 1] 及之前的元素,B[j] 及之前的元素)、同时小于等于任何未取到的元素
对于我们要取出的 A[i],有 A[i] <= B[j],也就是 B[j] 及以后元素**应该**排在A[i] 的右侧,且**将会**排在 A[i] 的右侧,因此只需考虑 B[j - 1] 及其之前元素。对于这部分元素,严格有 A[i] > B[j - 1],即「 A[i] 应该在 B[j - 1] 等元素的右侧」,且由于所有的 A[] 中元素一开始都在 B[] 中元素左侧,所以 B[j - 1] 到 B[0] 这部分元素的个数 j ,就是 A[i] 对应的逆序对个数。
注意,我们总是可以只统计某一侧数组的逆序对(比如只关心 A[]),而不用考虑另一侧。这是因为逆序对总是成对出现的,并且数组内部有序保证了内部逆序对的个数为 0,因此逆序对的元素一定是分别出现在 A[]、B[] 当中,我们只需以其中一个数组为准进行统计即可。
时间复杂度:O(nlogn)
空间复杂度:O(n)
实现:
1 | class Solution { |
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.