1.题目
给定一个长度为n的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1≤n≤100000
输入样例:
6
2 3 4 5 6 1
输出样例:
5
2.解题
利用归并排序的思想,在每次合并左右两个子数组时计算逆序对的数量
方法一:LeetCode
java:
public class reversePairs {
public static int reversePairs1(int[] arr, int n) {
if (arr.length < 2)
return 0;
int[] copy = new int[n];
for (int i = 0; i < n; i++) {
copy[i] = arr[i];
}
int[] tmp = new int[n];
return reversePairs2(arr, 0, n - 1, tmp);
}
// 递归求解左子数组和右子数组
public static int reversePairs2(int[] nums, int left, int right, int[] tmp) {
if (left == right)
return 0;
int mid = left + (right - left) / 2;
int leftPairs = reversePairs2(nums, left, mid, tmp);
int rightPairs = reversePairs2(nums, mid + 1, right, tmp);
// 如果有序,则不存在逆序对,直接返回
if (nums[mid] <= nums[mid + 1])
return leftPairs + rightPairs;
int crossPairs = merge(nums, left, mid, right, tmp);
return leftPairs + rightPairs + crossPairs;
}
// 合并
public static int merge(int[] nums, int left, int mid, int right, int[] tmp) {
for (int i = left; i <= right; i++)
tmp[i] = nums[i];
int i = left, j = mid + 1, count = 0, k = left;
while (k <= right) {
if (i == mid + 1) {
nums[k++] = tmp[j++];
} else if (j == right + 1) {
nums[k++] = tmp[i++];
} else if (tmp[i] <= tmp[j]) {
nums[k++] = tmp[i++];
} else {
nums[k++] = tmp[j++];
// 计算逆序对数量
count += mid - i + 1;
}
}
return count;
}
public static void main(String[] args) {
int n = 10;
int[] arr = { 88, 71, 16, 2, 72, 38, 94, 50, 72, 67 };
System.out.print(reversePairs1(arr, n));
}
}
复杂度
记序列长度为 n。
时间复杂度:同归并排序 O(nlogn)。
空间复杂度:同归并排序 O(n),因为归并排序需要用到一个临时数组。
方法二:在归并排序的基础上改进
java:
package reversePairs;
public class reversePairs2 {
public static long mergeSort(int[] arr, int low, int high) {
if (low >= high)
return 0;
int mid = (low + high) / 2;
return mergeSort(arr, low, mid) + mergeSort(arr, mid + 1, high) + merge(arr, low, mid, high);
}
public static long merge(int[] arr, int low, int mid, int high) {
int[] tmp = new int[high - low + 1];
int i = low, j = mid + 1, k = 0;
long res = 0;
while (i <= mid && j <= high) {
if (arr[i] <= arr[j]) {
tmp[k++] = arr[i++];
} else {
res += mid - i + 1; // 计算逆序对数
tmp[k++] = arr[j++];
}
}
while (i <= mid) {
tmp[k++] = arr[i++];
}
while (j <= high) {
tmp[k++] = arr[j++];
}
for (int l = 0; l < high - low + 1; l++) {
arr[l + low] = tmp[l];
}
return res;
}
public static void main(String[] args) {
int[] arr = { 88, 71, 16, 2, 72, 38, 94, 50, 72, 67 };
System.out.println(mergeSort(arr, 0, 9));
}
}
c++:
#include <iostream>
using namespace std;
long mbrlen(int q[], int l, int mid, int r)
{
int tmp[r - l + 1];
int i = l, j = mid + 1, k = 0;
long res = 0;
while (i <= mid && j <= r)
{
if (q[i] <= q[j])
{
tmp[k++] = q[i++];
}
else
{
res += mid - i + 1;
tmp[k++] = q[j++];
}
}
while (i <= mid)
tmp[k++] = q[i++];
while (j <= r)
tmp[k++] = q[j++];
for (i = l, j = 0; i <= r; i++, j++)
q[i] = tmp[j];
return res;
}
long reversePairs(int arr[], int l, int r)
{
if (l >= r)
return 0;
int mid = (l + r) / 2;
return reversePairs(arr, l, mid) + reversePairs(arr, mid + 1, r) + mbrlen(arr, l, mid, r);
}
int main()
{
int n = 0;
cin >> n;
int arr[] = {88, 71, 16, 2, 72, 38, 94, 50, 72, 67};
cout << reversePairs(arr, 0, n - 1) << endl;
return 0;
}