归并排序例题——逆序对的数量

做道简单一点的题巩固一下

归并排序实现步骤

  1. 将整个区间 [l, r] 划分为 [l, mid] 和 [mid+1, r]。
  2. 递归排序 [l, mid] 和 [mid+1, r]。
  3. 将左右两个有序序列合并为一个有序序列。

题目描述

给定一个长度为 n 的整数数列,请计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<ja[i]>a[j],则其为一个逆序对;否则不是。

输入格式

输入共两行。
第一行包含整数 n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。

输出格式

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

数据范围

1≤n≤100000
数列中的元素的取值范围 [1,1e9]。

输入样例

6
2 3 4 5 6 1

输出样例

5

具体实现

实现思路:

可以将所有的逆序对整体分为3大类

  1. 两个数同时出现在左半边(红色);
  2. 两个数同时出现在右半边(绿色);
  3. 一个数在左半边,一个数在右半边(黄色)。

在这里插入图片描述
因此,我们在归并排序的同时便要记录逆序对的个数。

  1. 红色情况时逆序对数量:merge_sort(l,mid);
  2. 绿色情况时逆序对数量:merge_sort(mid+1,r);
  3. 黄色情况时逆序对数量:先将左右两边分别变为有序序列,然后双指针进行比较,先选取右边序列当中第一个元素,判断左边序列当中有几个元素大于他,便有几个逆序对(即分别选取右边序列中的每一个元素,然后分别遍历左边序列当中的每个元素,进行比较判断,最后累加起来)。

代码注解:

  • n的最大值为100000,我们计算最坏情况,即数列时降序排列,就一共有 n(n-1)/2 个逆序对,即 5*1e9 个逆序对,可能会有大于 int 值的情况,因此使用 long long 进行存储。
  • 因为左右两个均为有序数列,所有当左边序列第 i 个元素大于右边序列第 j 个元素的时候,左边序列 [i,mid] 都严格大于右边序列第 j 个元素,即 mid-i+1 个逆序对,就是我们归并排序归并的过程,所以每当我们输出一个 q[i]>q[j] 的情况,便在逆序对个数上加一个 mid-i+1 。
  • 要注意 merge_sort 的返回值类型变为 long long ,否则会造成数据过多时无法AC。

实现代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=100010;
int n;
int q[N],temp[N];
ll merge_sort(int q[],int l,int r)
{
    if(l>=r)
    {
        return 0;
    }
    else
    {
        int mid=l+r>>1;
        ll res=merge_sort(q,l,mid)+merge_sort(q,mid+1,r);
        
        int k=0,i=l,j=mid+1;
        while(i<=mid&&j<=r)
        {
            if(q[i]<=q[j])
            {
                temp[k]=q[i];
                k++;
                i++;
            }
            else
            {
                temp[k]=q[j];
                k++;
                j++;
                res+=mid-i+1;
            }
        }
        while(i<=mid)
        {
            temp[k]=q[i];
            k++;
            i++;
        }
        while(j<=r)
        {
            temp[k]=q[j];
            k++;
            j++;
        }
        for(i=l,j=0;i<=r;i++,j++)
        {
            q[i]=temp[j];
        }
        return res;
    }
}
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>q[i];
    }
    cout<< merge_sort(q,0,n-1)<<endl;
    system("pause");
    return 0;
}
归并排序是一种分治算法,它的基本思想是将一个大问题分解成若干个小问题,然后递归地解决这些小问题,最后将它们合并成一个整体的解。在归并排序中,我们可以通过在合并两个有序数组的过程中统计逆序对数量求解逆序对。 具体来说,我们可以将原数组分成左右两个子数组,然后递归地对左右两个子数组进行排序,并统计左右两个子数组内部的逆序对数量。接着,我们再将左右两个子数组合并成一个有序数组,并统计左右两个子数组之间的逆序对数量。最后,我们将左右两个子数组的逆序对数量加起来,就得到了原数组的逆序对数量。 下面是使用归并排序逆序对的代码实现: ```python def merge_sort(arr): if len(arr) <= 1: return arr, 0 mid = len(arr) // 2 left, left_count = merge_sort(arr[:mid]) right, right_count = merge_sort(arr[mid:]) merged, merge_count = merge(left, right) return merged, left_count + right_count + merge_count def merge(left, right): i, j = 0, 0 count = 0 merged = [] while i < len(left) and j < len(right): if left[i] <= right[j]: merged.append(left[i]) i += 1 else: merged.append(right[j]) j += 1 count += len(left) - i merged += left[i:] merged += right[j:] return merged, count ``` 其中,`merge_sort` 函数用于对数组进行归并排序,并返回排序后的数组和逆序对数量。`merge` 函数用于合并两个有序数组,并统计它们之间的逆序对数量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虚心求知的熊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值