数组中的逆序对

本文探讨了如何利用归并排序的特性统计数组中的逆序对,通过两种统计方法以及树状数组的数据结构,实现对逆序对的快速计数。通过实例和代码展示了如何在Java中实现这两种方法,适合理解排序算法和数据结构在实际问题中的应用。
摘要由CSDN通过智能技术生成

数组中的逆序对

解题思路

归并排序

归并排序将数组分割为若干有序的子段,并进行两两“向上”合并。

在一次归并过程中,第一段数组和第二段数组均为有序的,只需要统计归并过程中数组中==“失序”==的数组元素就能够得到逆序对数。假设p1p2分别指向第一段数组A和第二段数组B的起始位置,且数组AB归并到数组C中。则存在两种不同的统计方式。

  • 统计方式1:倘若 A [ p 1 ] < B [ p 2 ] A[p1]<B[p2] A[p1]<B[p2],选择 A [ p 1 ] A[p1] A[p1]作为下一个归并到数组C中的元素,易知 A [ p 1 ] > B [ m i d + 1 ⋯ p 2 − 1 ] A[p1]>B[mid+1\cdots p2-1] A[p1]>B[mid+1p21],所以一共有 p 2 − ( m i d + 1 ) p2-(mid+1) p2(mid+1)个逆序的数。
  • 统计方式2:倘若 A [ p 1 ] > B [ p 2 ] A[p1]>B[p2] A[p1]>B[p2],选择 B [ p 2 ] B[p2] B[p2]作为下一个归并到数组C中段元素,易知 A [ p 1 ⋯ m i d ] > B [ p 2 ] A[p1 \cdots mid]>B[p2] A[p1mid]>B[p2],所以一共有 m i d − p 1 + 1 mid-p1+1 midp1+1个逆序的元素
树状数组

使用二分查找+排序来对数组进行离散化,使用桶来保存元素的信息,桶中保存元素出现的次数,前缀和即表示为所有小于该元素的个数。

从后向前进行操作,假设目前的遍历的元素为 a i a_i ai,利用前缀和,查找所有小于 a i a_i ai的个数,然后将 a i a_i ai加入到树状数组中去。

AC代码

归并排序

class Solution {
    private static int[] temp = null;

    public static int reversePairs(int[] nums) {
        temp = new int[nums.length];
        int res = mergeSort(nums, 0, nums.length - 1);
        return res;
    }

    public static int mergeSort(int[] nums, int left, int right) {
        if (left >= right)
            return 0;
        int mid = left + (right - left) / 2;
        int leftPairs = mergeSort(nums, left, mid);
        int rightPairs = mergeSort(nums, mid + 1, right);

        int crossPairs = merge(nums, left, mid, right);

        return leftPairs + crossPairs + rightPairs;
    }

    public static int merge(int nums[], int left, int mid, int right) {
        int p1 = left, p2 = mid + 1;
        for (int i = left; i <= right; i++) {
            temp[i] = nums[i];
        }
        int cnt = left;
        int ans = 0;
        while (p1 <= mid && p2 <= right) {
            if (temp[p1] <= temp[p2]) {
                nums[cnt] = temp[p1];
                p1++;
                // 统计方法1
                ans += p2 - (mid + 1);
            } else {
                nums[cnt] = temp[p2];
                p2++;
                // 统计方法2
                // ans += (mid-p1+1);
            }
            cnt++;
        }
        if (p1 <= mid) {
            while (p1 <= mid) {
                nums[cnt++] = temp[p1++];
                // 统计方法1
                ans += (p2 - mid - 1);
            }
        }
        if (p2 <= right) {
            while (p2 <= right) {
                nums[cnt++] = temp[p2++];
                // 统计方法2
                // ans += (mid-p1+1);
            }
        }
        return ans;
    }
}

树状数组

class Solution {
    public static int reversePairs(int[] nums) {
        int n = nums.length;
        int[] temp = new int[n];
        System.arraycopy(nums, 0, temp, 0, n);
        Arrays.sort(temp);
        for (int i = 0; i < n; i++) {
            nums[i] = Arrays.binarySearch(temp, nums[i]) + 1;
        }
        BIT bit = new BIT(nums.length);
        int ans = 0;
        for (int i = n - 1; i >= 0; i--) {
            ans += bit.query(nums[i] - 1);
            bit.update(nums[i]);
        }
        return ans;
    }
}

class BIT {
    private int[] tree;
    private int n;

    BIT(int n) {
        tree = new int[n + 1];
        this.n = n;
    }

    public int lowbit(int x) {
        return x & (-x);
    }

    public void update(int x) {
        for (int i = x; i <= n; i += lowbit(i)) {
            ++tree[i];
        }
    }

    public int query(int x) {
        int res = 0;
        for (int i = x; i > 0; i -= lowbit(i)) {
            res += tree[i];
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值