归并排序 与 逆序对数量

一、归并排序

 题目:

        给定你一个长度为 n 的整数数列,请你使用归并排序对这个数列按照从小到大进行排序,并将排好序的数列按顺序输出。

输入格式:

        输入共两行,第一行包含整数 n。

        第二行包含 n个整数(所有整数均在 1∼1e9 范围内),表示整个数列。

数据范围:

        0 < n < 100000

解题思路:

        如果单纯做出来这道题方法有很多种,只要要求时间复杂度为O(n log n)即可。使用内置函数sort排序也可以AC,但是这道题目要求咱们使用归并排序,那咱们就以这道题为例讲一下归并排序。

        1.归并排序主要是通过将一个数组进行log n次分解,直至每个数组元素个数为1。以[1,5,3,4,6,2]数组为例,第一次分解为[1,5,3] 和 [4,6,2] 。数组元素个数不为1,继续将数组分解,第二次分解[1,5]和[3]和[4,6]和[2]。还有两个数组没有元素个数不为1。继续进行数组分解。第三次分解成[1]和[5]和[3]和[4]和[6]和[2]每个数组元素个数都已经为1。完成了归并排序的第一个环节。

        

        2. 第一步的铺垫已经完成,第二步就是需要把每个数组进行按照从大到小的顺序进行合并,第一次合并之后是两个元素,第二次合并就是二元素个数与一元素个数数组合并,此时用双指针法能够将两个数组元素的从小到大顺序排列出来。还是拿[1,5,3,4,6,2]数组为例,第一步得到的是[1]和[5]和[3]和[4]和[6]和[2],第一次合并为[1,5] 和[3]和[4,6]和[2]。第二次合并为[1,3,5]和[2,4,6]。第三次合并为[1,2,3,4,5,6]。

 log n次分解、log n合并为归并排序

动图展示

实现代码 

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int q[N];
int temp[N];
void mergeSort(int q[],int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)/2;
    mergeSort(q,l,mid);
    mergeSort(q,mid+1,r);
    int k=0;
    int i=l;
    int j=mid+1;
    while(i<=mid&&j<=r)
    {
        if(q[i]<=q[j])
        {
            temp[k++]=q[i++];
        }
        else{
            temp[k++]=q[j++];
        }
    }
    while(i<=mid)
    {
        temp[k++]=q[i++];
    }
    while(j<=r)
    {
        temp[k++]=q[j++];
    }
    for(int a=l,b=0;a<=r;a++,b++)
    {
        q[a]=temp[b];
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>q[i];
    }
    mergeSort(q,0,n-1);
    for(int i=0;i<n;i++)
    {
        cout<<q[i]<<" ";
    }
}

 二、逆序对数量

题目:

        给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。逆序对的定义如下:对于数列的第 i 个和第 j个元素,如果满足 i<j且 a[i]>a[j],则其为一个逆序对;否则不是。

输入格式

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

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

输出格式

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

数据范围

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

解题思路:

        题目理解起来很容易,最经典的就是O(N*N)做法,只不过本题中数据范围100000,最大时间复杂度为O(n log n)。

        我们可以用上面讲的归并排序,第一步是将每个元素单独分成一个数组,右边的肯定要比左边的下标大,如果左边元素大于右边,即满足逆序对的条件,可以算出逆序对数量,再将所有逆序对的数量加起来即是结果。

代码实现:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int q[N];
int temp[N];
long long mergeSort(int q[],int l,int r)
{
    if(l>=r) return 0;
    int mid=(l+r)/2;

    long long ans=mergeSort(q,l,mid)+mergeSort(q,mid+1,r);
    int k=0;
    int i=l;
    int j=mid+1;
    while(i<=mid&&j<=r)
    {
        if(q[i]<=q[j])
        {
            temp[k++]=q[i++];
        }
        else{
            temp[k++]=q[j++];
            ans+=mid-i+1;
        }
    }
    while(i<=mid)
    {
        temp[k++]=q[i++];
    }
    while(j<=r)
    {
        temp[k++]=q[j++];
    }
    for(int a=l,b=0;a<=r;a++,b++)
    {
        q[a]=temp[b];
    }
    return ans;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>q[i];
    }
   // mergeSort(q,0,n-1);
    /*for(int i=0;i<n;i++)
    {
        cout<<q[i]<<" ";
    }*/
    cout<<mergeSort(q,0,n-1)<<endl;
}

猜你喜欢

转载自blog.csdn.net/m0_63743577/article/details/131541178