归并排序思想应用之逆序数对

归并排序是一种基本的排序,简洁的归并排序写出来是最基本的要求, 要做到一遍可以运行不出错也是很不容易,需要天天复习,阿里的面试当中让写一道程序就是关于归并排序的.很遗憾由于当时太紧张写了选择排序,脑子坏掉了感觉.
更加重要的是,归并排序是分而治之的思想.这种思想才是解题的根源.

剑指offer那道逆序对的题目做出来其实不容易.看一下这道题目的分析吧.

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007

题目分析

基本思想是分而治之(有位牛客朋友说的特别好,思想是分而治之而不是归并排序,这才是真谛),具体体现类似归并排序,一个数组分成A B两段, 先对A和B分别求逆序对c1 ,c2,然后合并两个有序数组得到逆序對c3, 三者的和就是最终的结果.最关键的地方在于合并的过程.
我们由两个数组从后往前遍历进行归并,当遇到A[i] > B[i]的时候,B[i]及其前边有几个数,当前A[i]久产生几个逆序對.其余的情况(A[i]<=B[i]或两者之一先到达数组头)均不产生逆序数. 想到以A[i]为参照元素来找逆序对这点很重要. 写程序才能简洁.

另外一方面是数据量较大需要取模,这时候的索引要注意,最好选用long long类型比较保险.

代码

class Solution {
public:
  long long InversePairs(vector<int > &data) {
     return mergeSort(data , 0 , data.size()-1)%1000000007;
    }
  long long  Mmerge(vector<int> &a , long long i ,long long  mid , long long j ){   
        vector<int> res;
        long long cc = 0;
        long long si = mid, sj = j;
        while(si >= i  &&  sj >= mid+1){
            if( a[si] > a[sj]){
               res.push_back(a[si]);
               cc=(cc - mid+ sj)%1000000007;
               si--;
            }
            else{
               res.push_back(a[sj]);
               sj--;
            }
        }

        while(si >= i)
        {
            res.push_back(a[si]);
            si--;
        }
        while(sj >= mid+1)
        {
            res.push_back(a[sj]);
            sj--;
        }

        for(long long ssi = i ; ssi <=j ; ssi++ ){
            a[ssi] = res[j-ssi];
        } 

        return cc;
    }
    long long  mergeSort(vector<int> &a , int i  , int j ){
       if(i >= j){
            return 0;
       }
       long long mid = (i + j)/2;
       // 左闭右开
       long long cc1 =mergeSort(a, i , mid);
       long long  cc2 =mergeSort(a, mid+1 , j);
       long long cc =Mmerge(a, i , mid , j);
       return (cc+cc1)%1000000007+cc2%1000000007;
    }
};

posted @ 2017-07-30 21:54  bzt007  阅读(166)  评论(0编辑  收藏  举报