归并排序、求逆序对(offer51)

在这里插入图片描述
本题求数组中的逆序对,很自然的想到了两层for循环,然而困难题,一定超时!

在评论区看到了一种利用归并排序“治”的过程,顺带将逆序对求出来了,记录一下,顺便将归并排序和快排做一个比较。

归并排序

在这里插入图片描述
正如图所示,归并排序是将原数组不断的划分,然后开始合并,有点二分法的意思;到只剩一个元素的时候,开始合并,将小的有序数组合并为大的有序数组

恰好,这个合并的时候,如果加一个比较的步骤,就可以求出逆序对的个数,例如:
原本的数组是:【7,3,2,6,0,1,5,4】
原本的方法是:将7与余下的数字进行比较,得出逆序次数,余下的元素也是一样的操作;
归并排序的方法:7仍然要跟所有的元素进行比较(效果上面),但是如果它的前面有比7小的数字,这个比较的过程就可以省略(实现上面)

7首先和3比较,得出逆序对1;(2,6)、(0,1)、(5,4)也相应的进行比较;
然后3和2进行比较,3比2大,所以3后面的数字也比2大(小数组已经有序了),因此逆序个数=3及3后面的元素个数;间接得到了7大于2的结果,所以时间复杂度降下来了嘛!

cpp实现
#include <bits/stdc++.h>
using namespace std;
/*
归并排序,一般也可用于求逆序对
*/
class Solution 
{
private:
    static void MergeSort(int left,int right,vector<int>& nums,vector<int>& tmp)
    {
        {
        //分
        if(left>=right)//只有一个元素的时候开始返回
            return;
        int m=left+(right-left)/2;
        MergeSort(left,m,nums,tmp);
        MergeSort(m+1,right,nums,tmp);
        //治
        for(int k=left;k<=right;++k)
        {
            tmp[k]=nums[k];
        }
        int i=left,j=m+1;
        for(int k=left;k<=right;++k)
        {
            //如果i越界了,就将nums[j]添加进tmp
            if(i==m+1)
                nums[k]=tmp[j++];
            //如果j越界了,就将nums[i]添加进tmp
            //nums[i]<=nums[j],将nums[i]添加进tmp
            else if(j==right+1||tmp[i]<=tmp[j])
                nums[k]=tmp[i++];
            else//nums[i]>nums[j],将nums[j]添加进tmp
            {
                nums[k]=tmp[j++];
            }    
        }
    }
    }
    static int sort(int left,int right,vector<int>& nums,vector<int>& tmp)
    {
        //分
        if(left>=right)//只有一个元素的时候开始返回
            return 0;
        int m=(right+left)/2;
        int res=sort(left,m,nums,tmp)+sort(m+1,right,nums,tmp);
        //治
        for(int k=left;k<=right;++k)
        {
            tmp[k]=nums[k];
        }
        int i=left,j=m+1;
        for(int k=left;k<=right;++k)
        {
            //如果i越界了,就将nums[j]添加进tmp
            if(i==m+1)
                nums[k]=tmp[j++];
            //如果j越界了,就将nums[i]添加进tmp
            //nums[i]<=nums[j],将nums[i]添加进tmp
            else if(j==right+1||tmp[i]<=tmp[j])
                nums[k]=tmp[i++];
            else//nums[i]>nums[j],将nums[j]添加进tmp,同时逆序对的数量+1;
            {
                nums[k]=tmp[j++];
                res+=m-i+1;
            }    
        }
        return res;
    }
public:
    static int reversePairs(vector<int>& nums) 
    {
        vector<int> tmp(nums.size());
        return sort(0,nums.size()-1,nums,tmp);
    }
    static void TestMergeSort(vector<int>& nums) 
    {
        vector<int> tmp(nums.size());
        MergeSort(0,nums.size()-1,nums,tmp);
    }
};

int main() 
{
    vector<int> nums={1,3,2,5,6,8};
    vector<int> nums2={1,3,2,5,6,8};
    Solution::TestMergeSort(nums);
    int c=Solution::reversePairs(nums2);
    return 0;
}

与快速排序进行比较

快速排序是每次选择一个基准数据,将原数组分为两部分,左边比它小,右边比它大,然后继续将左右两边的数组进行排序;

快速排序利用了问题的信息,如果,每次都是在中间位置进行划分,那么快排的时间复杂度是O(Nlog2N),但如果,每次划分的位置在首部,那么时间复杂度就变成了O(N2),因此是不稳定的;

归并排序,在分的过程中,一直是对半分,没有利用问题的信息;在合的过程中,才开始利用问题的信息;

快速排序cpp实现
void QuickSort2(vector<int>& arr,int left,int right){
    //快速排序,每次选取第一个元素作为基准数据,将数组分为两个部分,
    //左边的元素都小于基准数据,右边的元素都大于基准数据。
    if(left>=right)
        return;
    int low=left;
    int high=right;
    while(low<high){
        while(low<high&&arr[high]>=arr[left])
            high--;
        while(low<high&&arr[low]<=arr[left])
            low++;
        swap(arr[low],arr[high]);
    }
    swap(arr[left],arr[low]);
    QuickSort2(arr,left,low-1);
    QuickSort2(arr,low+1,right);
}

end!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值