剑指Offer-32——数组中的逆序对

题目描述

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

输入描述:

题目保证输入的数组中没有的相同的数字

数据范围:

对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5

示例1

输入
1,2,3,4,5,6,7,0
输出
7

解题思路

首先我很纠结一个问题,这个树是大于后面的所有数呢?还是只是大于后面的一个数呢?后来转念一想,自己真的傻,要是只大于后面所有数字,那不是只有一个才能满足条件吗?最后的逆序对不就是只有一个?所有肯定是该数字大于它后面的数字,这样的两个数组成逆序对啊!

然后,想明白这里,直接开干。emmm,直接一个类似于冒泡排序的玩意就来了。果然超时了,时间复杂的为O(n^n)了。。。

错误代码

public class Solution {
    public int InversePairs(int [] array) {
        int P = 0;
        for(int i = 0;i<array.length;i++){
            for(int j = i; j< array.length;j++){
                if(array[i]>array[j]){
                    P++;
                }
            }
        }
         int result = P%1000000007;
        return result;
    }
}

重新整理思路

既然这种方法可行,那说明只需要改进“排序”的方式就ok了啊。于是尝试一手归并求。
复习归并排序的传送门:面试必备–八大排序算法

归并排序的算法

//归并排序
	public static void mergeSort(int[] arr,int low,int high) {
		int middle=(high+low)/2;
		if(low<high) {
			//处理左边
			mergeSort(arr, low, middle);
			//处理右边
			mergeSort(arr, middle+1, high);
			//归并
			merge(arr,low,middle,high);
		}
	}
	
	public static void merge(int[] arr,int low,int middle, int high) {
		//用于存储归并后的临时数组
		int[] temp = new int[high-low+1];
		//记录第一个数组中需要遍历的下标
		int i=low;
		//记录第二个数组中需要遍历的下标
		int j=middle+1;
		//用于记录在临时数组中存放的下标
		int index=0;
		//遍历两个数组取出小的数字,放入临时数组中
		while(i<=middle&&j<=high) {
			//第一个数组的数据更小
			if(arr[i]<=arr[j]) {
				//把小的数据放入临时数组中
				temp[index]=arr[i];
				//让下标向后移一位;
				i++;
			}else {
				temp[index]=arr[j];
				j++;
			}
			index++;
		}
		//处理多余的数据
		while(j<=high) {
			temp[index]=arr[j];
			j++;
			index++;
		}
		while(i<=middle) {
			temp[index]=arr[i];
			i++;
			index++;
		}
		//把临时数组中的数据重新存入原数组
		for(int k=0;k<temp.length;k++) {
			arr[k+low]=temp[k];
		}
	}

正确的代码

我承认 这段代码 是别人的。因为我完全没理解为什么但是在递归的时候,就计算了结果的大小 cnt = (cnt + (mid-i+1))%1000000007;,而不是求出逆序个数在进行求模运算。。。难道递归不会影响到结果吗?噢。。。原来是每个结果求模相加结果也不变啊。。


public class Solution {
    private int cnt;
    private void MergeSort(int[] array, int start, int end){
        if(start>=end)return;
        int mid = (start+end)/2;
        MergeSort(array, start, mid);
        MergeSort(array, mid+1, end);
        MergeOne(array, start, mid, end);
    }
    private void MergeOne(int[] array, int start, int mid, int end){
        int[] temp = new int[end-start+1];
        int k=0,i=start,j=mid+1;
        while(i<=mid && j<= end){
//如果前面的元素小于后面的不能构成逆序对
            if(array[i] <= array[j])
                temp[k++] = array[i++];
            else{
//如果前面的元素大于后面的,那么在前面元素之后的元素都能和后面的元素构成逆序对
                temp[k++] = array[j++];
                cnt = (cnt + (mid-i+1))%1000000007;
            }
        }
        while(i<= mid)
            temp[k++] = array[i++];
        while(j<=end)
            temp[k++] = array[j++];
        for(int l=0; l<k; l++){
            array[start+l] = temp[l];
        }
    }
    public int InversePairs(int [] array) {
        MergeSort(array, 0, array.length-1);
        return cnt;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值