逆序对个数【归并排序的应用】【分治】

题目描述

题目链接【逆序对个数】

貌似出现了无法跳转的情况:https://www.acwing.com/problem/content/790/

acwing官网的题库中有,可以去找找。

给定一个长度为 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\leq{n}\leq{100000} 1n100000
数列中的元素的取值范围 [ 1 , 1 0 9 ] [1,10^9] [1,109]

输入样例:
6
2 3 4 5 6 1
输出样例:
5

解题思路与代码

一般看到这个题,第一想到的肯定是暴力比较,然后计算逆序对的个数,一方面这个时间复杂度为 O ( n 2 ) O(n^2) O(n2),另一方面,这个过程中只能进行计数,不能存储一些有用的信息。

其实可以想一下归并排序,归并排序的过程是:把一个数组不断的划分成两个数组,利用dfs的思想,划分到最底层,也就是数组中只有一个元素的时候,这个时候数组肯定是有序的,之后就可以进行合并了。合并的过程是从底部向上进行合并的。我们就可以利用归并排序中“并”的过程计算逆序对的个数。

在比较的过程中:

  • a r r [ j ] < a r r [ i ] : arr[j]<arr[i]: arr[j]<arr[i]: 那么前面的有序数组的剩余的个数, a r r [ j ] arr[j] arr[j]的逆序对个数; [ l e f t , m i d ] [left,mid] [left,mid], n u m = m i d − i + 1 num=mid-i+1 num=midi+1;
  • a r r [ j ] > a r r [ i ] : arr[j]>arr[i]: arr[j]>arr[i]: 逆序对的个数肯定为0,首先在前面的有序数组中, a r r [ i ] arr[i] arr[i]后面的肯定都比 a r r [ i ] arr[i] arr[i]大,其次, a r r [ j ] arr[j] arr[j]已经大于 a r r [ i ] arr[i] arr[i]了,那么 a r r [ j ] arr[j] arr[j]之后的元素肯定也是大于 a r r [ i ] arr[i] arr[i]的,所以, a r r [ i ] arr[i] arr[i]之后的元素肯定都比 a r r [ i ] arr[i] arr[i]大,所以不存在逆序对。

在这里插入图片描述

在这里插入图片描述

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int arr[100010];
int temp[100010];
ll num = 0;

void merge(int left, int mid, int right){
    int i = left;
    int j = mid + 1;
    int count = 0;
    while(i<=mid && j<=right){
        if(arr[i]<=arr[j]){
            temp[count++] = arr[i++];
        }else{
            //只需要在归并排序的基础上,加这一行代码就够了
            num += (mid - i + 1);
            temp[count++] = arr[j++];
        }
    }
    while(i<=mid){
        temp[count++] = arr[i++];
    }
    while(j<=right){
        temp[count++] = arr[j++];
    }
    
    count = 0;
    while(left<=right){
        arr[left++] = temp[count++];
    }
}
void mergeSort(int left, int right){
    if(left >= right) return;
    
    int mid = left + (right - left)/2;
    
    mergeSort(left, mid);
    mergeSort(mid+1, right);
    
    merge(left, mid, right);
    
}
int main(){
    int n;
    cin>>n;
    for(int i=0; i<n; i++){
        cin>>arr[i];
    }
    mergeSort(0, n-1);
    cout<<num<<endl;
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值