归并排序、求逆序对(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!

刷题总结类 文章被收录于专栏

这里记录一些刷题时候的总结思考

全部评论

相关推荐

11-03 14:38
重庆大学 Java
AAA求offer教程:我手都抬起来了又揣裤兜了
点赞 评论 收藏
分享
11-09 14:54
已编辑
华南农业大学 产品经理
大拿老师:这个简历,连手机号码和照片都没打码,那为什么关键要素求职职位就不写呢? 从上往下看,都没看出自己到底是产品经理的简历,还是电子硬件的简历? 这是一个大问题,当然,更大的问题是实习经历的描述是不对的 不要只是去写实习流程,陈平,怎么去开会?怎么去讨论? 面试问的是你的产品功能点,是怎么设计的?也就是要写项目的亮点,有什么功能?这个功能有什么难处?怎么去解决的? 实习流程大家都一样,没什么优势,也没有提问点,没有提问,你就不得分 另外,你要明确你投的是什么职位,如果投的是产品职位,你的项目经历写的全都是跟产品无关的,那你的简历就没用 你的面试官必然是一个资深的产品经理,他不会去问那些计算机类的编程项目 所以这种四不像的简历,在校招是大忌
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务