4.逆序对的数量

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

剑指 Offer 51. 数组中的逆序对

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值