统计序列中的逆序对
目录
因为好像做过这个题目,所以稍微提一下。最简单的方式就是归并排序 题解 方法分别是归并排序和树状数组。
归并排序
代码来源:https://blog.csdn.net/afei__/article/details/82951905 我觉得这个写的好一些。并且官方题解里面是写反的,我一开始还看了半天。
public class Main {
public static void main(String[] args) {
int[] arr = new int[] { 2, 3, 8, 6, 1 };
int inversionCount = mergeSort(arr, 0, arr.length);
printArray(arr);
System.out.println("inversionCount: " + inversionCount);
}
public static int mergeSort(int[] arr, int start, int end) {
int inversionCount = 0;
int length = end - start;
if (length > 1) { // 长度大于1才需要排序
int mid = (start + end) / 2;
inversionCount += mergeSort(arr, start, mid); // sort left
inversionCount += mergeSort(arr, mid, end); // sort right
inversionCount += merge(arr, start, mid, end);
}
return inversionCount;
}
public static int merge(int[] arr, int start, int mid, int end) {
// check input
if (arr == null || start < 0 || end > arr.length) {
return 0;
}
int[] temp = new int[end - start];
int inversionCount = 0;
int i = start; // 左半部分索引
int j = mid; // 右半部分索引
int k = 0; // temp数组索引
while (i < mid && j < end) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
// 一旦 arr[i] > arr[j],就会有 (mid - i) 个逆序对产生
inversionCount += mid - i;
}
}
if (i != mid) {
System.arraycopy(arr, i, temp, k, mid - i);
}
if (j != end){
System.arraycopy(arr, j, temp, k, end - j);
}
System.arraycopy(temp, 0, arr, start, temp.length);
return inversionCount;
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
}
树状数组
思路是这样的,对于一个给定的数组a,比如[5,5,2,3,6],从后往前遍历,并统计其前缀和。 每加入一个数字,其添加的逆序对的个数就等于i-1位的前缀和。 以该例子作为示范,显然6,3,2都没有逆序,在输入第一个5的时候,其前缀和表示所有小于等于4的数字的数量,等于2; 以此类推,将逆序对求解转变为了求解动态前序和的问题。
由于前序和动态变化,最好的方式就是树状数组。使用官方代码
class BIT {
private:
vector<int> tree;
int n;
public:
BIT(int _n): n(_n), tree(_n + 1) {}
static int lowbit(int x) {
return x & (-x);
}
int query(int x) {
int ret = 0;
while (x) {
ret += tree[x];
x -= lowbit(x);
}
return ret;
}
void update(int x) {
while (x <= n) {
++tree[x];
x += lowbit(x);
}
}
};
class Solution {
public:
int reversePairs(vector<int>& nums) {
int n = nums.size();
vector<int> tmp = nums;//tmp保存前缀和
// 离散化,将具体数字转为对应的排名。说实话我来做的话可能只能开个map来存了,应该是想不到lower_bound的用法
sort(tmp.begin(), tmp.end());
for (int& num: nums) {
num = lower_bound(tmp.begin(), tmp.end(), num) - tmp.begin() + 1;
}
// 树状数组统计逆序对
BIT bit(n);
int ans = 0;
for (int i = n - 1; i >= 0; --i) {
ans += bit.query(nums[i] - 1);//求解n-1的前缀和
bit.update(nums[i]);//把i增加进去
}
return ans;
}
};