题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数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;
}
}