剑指Offer---面试题36:数组中的逆序对

Posted 范二er

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer---面试题36:数组中的逆序对相关的知识,希望对你有一定的参考价值。

一.题目

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。
例如:7,5,6,4,一共存在五个逆序对:7,5,7,6,7,4,5,4,6,4;


二.分析

思路1:显而易见,从头到尾扫描数组,没扫描到一个数字,就拿去和后面所有的数组进行比较,两个for循环,解决问题,算法时间复杂度为o(n^2),

思路2:跳跃性的联想到merge_sort,在merge函数中,我们有一个过程是这样的,对左(leftArray)右(rightArray)两个已经排好序的数组进行整合,使整合之后的数组任然是排好序的,经典的算法思路如下:

[Merge Sort算法 浙江大学 陈越老师的视频,好的不要不要的
—出自中国大学MOOC网]

  1. 一个索引(leftstart)指向左边数组的第一位,一个索引(rightStart)指向右边数组的第一位,一个索引(tmpPos)指向临时的第一位

  2. 将左右侧数组索引所指向的位置处的数字进行比较,将小的数组存放进临时数组,然后相应的移动指针.

  3. leftArray[leftStart]>rightArray[rightStart]时,试想,leftArray中的所有数字是不是都大于rightArray[rightStart]???(因为leftArray是排好序的),那么leftArray.length也就统计了一次逆序对.

  4. 当我们递归的过程进行完了,所有的逆序对也就统计完成了

  5. 经典归并排序的时间复杂度为0(nlogn)


四.代码

public class Solution 
    int count;

    public int InversePairs(int[] array) 
        int n=array.length;
        count = 0;
        int[]tmpA=new int[n];
        if (n != 0)          
            mergeSort(array,tmpA, 0, n - 1);
        return count;
    

    /**
     * 归并排序--递归形式
     *
     * @param a        待排序的数组
     * @param tmpA     临时创建的数组
     * @param leftStart左边数组的起始位置
     * @param rightEnd 右边数组的终止位置
     * @author ZFY
     */
    public void mergeSort(int[] a, int[] tmpA,int leftStart, int rightEnd) 
        if (leftStart>= rightEnd)
            return;
        int center = (leftStart+ rightEnd) /2; 
        mergeSort(a,tmpA, leftStart, center);//递归排序左边
        mergeSort(a,tmpA, center + 1, rightEnd); //递归排序右边
        merge(a,tmpA, leftStart, center+1, rightEnd);//归并
    

    /**
     * 归并过程 排序完成的序列在原序列a中
     *
     * @param a        待排序数组
     * @param tmpA     临时数组
     * @param leftStart  左边数组的起始位置
     * @param rightStart 右边数组的起始位置
     * @param rightEnd 右边数组的结束位置
     * @author ZFY
     */
    public void merge(int[] a,int[] tmpA, int leftStart, int rightStart, int rightEnd) 
        int leftEnd=rightStart-1;
        int tmpPos = leftStart;
        int eNum=rightEnd-leftStart+1;
        while (leftStart <= leftEnd && rightStart <= rightEnd) 
            if (a[leftStart] <= a[rightStart])
                tmpA[tmpPos++] = a[leftStart++];
            else 
                tmpA[tmpPos++] = a[rightStart++];
                count += leftEnd - leftStart + 1;   
                // 左右两侧的序列都是已经排好序的序列(递增),当左边的第一个数字大于右边第一个数字时
                //那个左侧序列的所有数字都大于右边第一个
               //例如: 4,5,6,7,8 3,4,5,6 左边的4大于右边的3,那么左侧所有的都大于3,
               //左侧所有的个数为leftEnd - leftStart + 1;

            
        

        while (leftStart <= leftEnd)
            tmpA[tmpPos++] = a[leftStart++];
        while (rightStart <= rightEnd)
            tmpA[tmpPos++] = a[rightStart++];
        for(int l=0;l<eNum;l++,rightEnd--)
            a[rightEnd]=tmpA[rightEnd];
        
    

将第50行代码去掉,剩下的代码就是一个完整的归并排序的递归算法.排好序的数组任然存放于原数组array中;


五.小结

由这个题目也可以看出来,归并排序算法说到底就是在消除序列中的逆序对,当序列中的逆序对为0的时候,这个序列就是已经被排好序了.

以上是关于剑指Offer---面试题36:数组中的逆序对的主要内容,如果未能解决你的问题,请参考以下文章

剑指offer-36:数组中的逆序对

剑指offer面试题51:数组中的逆序对

结合《剑指offer(第二版)》面试题51来谈谈归并排序

《剑指offer》第五十一题(数组中的逆序对)

剑指Offer对答如流系列 - 数组中的逆序对

[剑指offer]51-数组中的逆序对(归并排序)