剑指offer-数组中的逆序对

36 篇文章 0 订阅
8 篇文章 0 订阅

数组中的逆序对

题目描述

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

例如:

例如,有一个数组为Array[0..n] 其中有元素a[i],a[j].如果 当i< j时,a[i]>a[j],那么我们就称(a[i],a[j])为一个逆序对。在数组{7,5,6,4}中一共存在5对逆序对,分别是(7,6),(7,5),(7,4),(6,4),(5,4)。

解题思路

看到这样的题目,最简单的想法就是遍历每一个元素,让其与后面的元素对比,如果大于则count++,但是这样的时间复杂度是o(n2)。这题有更好的解决方法,时间复杂度只需要o(nlogn)。其实这道题目的思路跟归并排序差不多,求逆序对的过程就是一个求归并排序的过程,在求出逆序对以后,原数组变得有序,是通过归并排序得到的。

  1. 总体的意思就是将数组分成两段,首先求段内的逆序对数量,比如下面两段代码就是求左右两端数组段内的逆序对数量
    inversions+=InversePairsCore(arry,start,mid,temp);//找左半段的逆序对数目
    inversions+=InversePairsCore(arry,mid+1,end,temp);//找右半段的逆序对数目

  2. 然后求段间的逆序对数量,如下面的代码
    inversions+=MergeArray(arry,start,mid,end,temp);//在找完左右半段逆序对以后两段数组有序,然后找两段之间的逆序对。最小的逆序段只有一个元素。

  3. 然后在求段间逆序对的时候,我们分为arry[start…mid]和arry[mid+1…end],然后设置两个指针ij分别指向两段数组的末尾元素,也就是i=mid,j=end。然后比较arry[i]和arry[j]:

    • 如果arry[i]>arry[j],因为两段数组都是有序的,所以arry[i]>arry[mid+1…j],这些都是逆序对,我们统计出的逆序对为j-(mid+1)+1=j-mid。并且将大数arry[i]放入临时数组temp[]当中,i往前移动;
    • 如果arry[i]< arry[j],则将大数arry[j]放入temp[]中,j往前移。

仿造归并排序算法:

package com.genge.offer;

/**
 * Created by Genge on 2016-06-30.
 */
public class InversePairs {
    public int InversePairs(int [] array) {
        int[] temp = new int[array.length];
        return InversePairsCore(array, 0, array.length - 1, temp);
    }
    //分治
    private int InversePairsCore(int[] array, int start, int end, int[] temp) {
        int count = 0;
        if (start < end) {
            int mid = (start + end) / 2;
            count += InversePairsCore(array, start, mid, temp);  //前半段逆序数
            count += InversePairsCore(array, mid + 1, end, temp);  //后半段逆序数
            count += MergeArray(array, start, mid, end, temp);  //归并后整段逆序数
        }
        return count;
    }
    //合并
    // 将有二个有序数列a[first...mid]和a[mid...last]合并。  
    private int MergeArray(int[] array,int start,int mid,int end,int temp[]){
        int i = start, j = mid + 1;
        int m = mid, n = end;
        int k = 0, count = 0;
        while (i <= m && j <= n) {
            if (array[i] > array[j]) {
                temp[k++] = array[j++];
                // 【因为数组此时是有序数组】
                // 因为如果a[i]此时比右数组的当前元素a[j]大,  
                // 那么左数组中a[i]后面的元素就都比a[j]大  
                count += mid - i + 1;
            }else{
                temp[k++] = array[i++];
            }
        }
        while(i <= m ){
            temp[k++] = array[i++];
        }
        while (j <= n) {
            temp[k++] = array[j++];
        }
        System.arraycopy(temp, 0, array, start, k);//排序结果复制到源数组,用for循环也行
        return count;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值