Acwing---788.逆序对的数量

逆序对的数量

1.题目

给定一个长度为 n n n 的整数数列,请你计算数列中的逆序对的数量。逆序对的定义如下:对于数列的第 i i i个和第 j j j 个元素,如果满足

i < j i<j i<j 且 a [ i ] > a [ j ] a[i]>a[j] a[i]>a[j] ,则其为一个逆序对;否则不是。

输入格式

第一行包含整数 n n n,表示数列的长度。

第二行包含 n n n 个整数,表示整个数列。

输出格式

输出一个整数,表示逆序对的个数。

数据范围
1 ≤ n ≤ 100000 1≤n≤100000 1≤n≤100000,数列中的元素的取值范围 [ 1 , 1 0 9 ] [1,10^9] [1,109]。

输入样例:

6

2 3 4 5 6 1

输出样例:

5

2.基本思想

法1:双重for暴力 ------> 时间超时 O(n^2)
法2: 归并排序
基于归并排序的解法

一种高效的计算逆序对的方法是使用修改后的归并排序算法。基本思想是将数组分割为较小的子数组,递归地对它们进行排序,然后在合并过程中计算逆序对。

  • 归并排序步骤:将数组递归地分割为两半,直到每个子数组只有一个元素。然后,将子数组按照排序顺序合并回来,同时在合并过程中计算逆序对。

  • 合并过程中计算逆序对:在合并两个已排序的子数组时,如果发现逆序对(即 arr[i] > arr[j]),则将逆序对的数量递增为第一个子数组中剩余的元素数量(即 (mid - i + 1),其中 mid 是合并的子数组的中间索引)。

  • 这是因为在合并过程中,如果左边的子数组元素 arr[i] 大于右边的子数组元素 arr[j],则 arr[i] 大于右边子数组中的所有元素,形成逆序对。

示例

假设一个数组 arr = [4, 3, 2, 1]。

使用基于归并排序的方法,我们可以如下计算逆序对的数量:

初始数组:[4, 3, 2, 1]

归并排序步骤1:[4, 3, 2, 1] -> [4, 3], [2, 1]

归并排序步骤2:[4, 3], [2, 1] -> [4], [3], [2], [1]

归并排序步骤3:合并 [4], [3],得到[3, 4]

合并过程中计算逆序对: [4][3] 合并,总逆序对数量 = 1(因为 4 > 3 且 且 4 后面没有其他元素)

归并排序步骤4:合并 [2], [1], 得到[1, 2]

合并过程中计算逆序对:[2] [1] 合并,总逆序对数量 = 1(因为 2 > 1 且 且 2 后面没有其他元素)

归并排序步骤5:[3, 4], [1, 2] -> [1, 2, 3, 4]

合并过程中计算逆序对:[3, 4] [1, 2] 合并,总逆序对数量 = 4(最开始 3 和 1 比较,因为3 > 1, 所以逆序对为 mid - l + 1 = 1 - 0 + 1 = 2 个。 接下来3 与 2 比较:因为3 > 2, 所以逆序对为 mid - l + 1 = 1 - 0 + 1 = 2 个。)

最终逆序对数量:1 + 1 + 2 + 2 = 6

  • 时间复杂度:该方法的时间复杂度为 O(n log n)。

3.代码实现

csharp 复制代码
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) arr[i] = sc.nextInt();
        System.out.println((merge_sort(arr, 0, n - 1)));
    }

    private static long merge_sort(int[] arr, int l, int r) {
        if (l >= r) return 0;
        int mid = (l + r) >> 1;
        long res = 0;
        //子序列归并
        res += merge_sort(arr, l, mid) + merge_sort(arr, mid + 1, r);

        int i = l, j = mid + 1, k = 0;
        int[] temp = new int[r - l + 1];
        while (i <= mid && j <= r) {
            if (arr[i] <= arr[j]) temp[k++] = arr[i++];
            else {
                temp[k++] = arr[j++];
                res += mid - i + 1;
            }
        }
        while (i <= mid) temp[k++] = arr[i++];
        while (j <= r) temp[k++] = arr[j++];

        for (i = l, j = 0; i <= r; i++, j++) arr[i] = temp[j];
        return res;
    }
}
相关推荐
YuTaoShao2 分钟前
【LeetCode 每日一题】3634. 使数组平衡的最少移除数目——(解法一)排序+滑动窗口
算法·leetcode·排序算法
波波00710 分钟前
每日一题:.NET 的 GC是如何分代工作的?
算法·.net·gc
风暴之零20 分钟前
变点检测算法PELT
算法
深鱼~20 分钟前
视觉算法性能翻倍:ops-cv经典算子的昇腾适配指南
算法·cann
李斯啦果21 分钟前
【PTA】L1-019 谁先倒
数据结构·算法
梵刹古音26 分钟前
【C语言】 指针基础与定义
c语言·开发语言·算法
啊阿狸不会拉杆43 分钟前
《机器学习导论》第 5 章-多元方法
人工智能·python·算法·机器学习·numpy·matplotlib·多元方法
R1nG8631 小时前
CANN资源泄漏检测工具源码深度解读 实战设备内存泄漏排查
数据库·算法·cann
_OP_CHEN2 小时前
【算法基础篇】(五十六)容斥原理指南:从集合计数到算法实战,解决组合数学的 “重叠难题”!
算法·蓝桥杯·c/c++·组合数学·容斥原理·算法竞赛·acm/icpc
TracyCoder1232 小时前
LeetCode Hot100(27/100)——94. 二叉树的中序遍历
算法·leetcode