知识点 数组
描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007
数据范围: 对于 50\%50% 的数据, size\leq 10^4size≤104
对于 100\%100% 的数据, size\leq 10^5size≤105
数组中所有数字的值满足 0 \le val \le 10000000≤val≤1000000
要求:空间复杂度 O(n)O(n),时间复杂度 O(nlogn)O(nlogn)
输入描述:
题目保证输入的数组中没有的相同的数字
其实这道题目感觉蛮难的,刚开始完全没有思路。后面感觉这道题本质上应该是排序。数组中存在逆序,那假如我通过排序算法,在排序过程中每交换一次大的数到小的数后面,说明这两个数就是一个逆序,这样在排序过程中统计需要交换的次数就是逆序对数量。
下面是我尝试写的代码,通过率5/6,有一个超时了。也不知道对不对。
//逆序对的总数=数组中每个位置处数字的逆序对的和
//算法复杂度O(nlogn),那么应该是一个for循环嵌套一个二分排序
//感觉这本质是一个排序算法,把这个数组按照某种排序算法排成正序,每两两交换一次数据说明有一个逆序对??
//使用了这种排序算法,通过率是5/6, 并提示您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
int length = array.length;
int deOrderNum = 0; //统计逆序对数量
for (int i = 1; i < length; i++) {
for (int j = i; j>0 && (array[j-1] - array[j]) > 0 ; j--) {
int temp = array[j];
array[j] = array[j - 1];
array[j - 1] = temp;
deOrderNum = (deOrderNum + 1) % mod;
}
}
return deOrderNum % 1000000007;
看了大神的写法,用的是归并排序做的,看来自己方向是对的,这道题本质上是排序。
需要去学习一下归并排序的算法。这里我在看了算法演进图后尝试写一个。算法演进图如下
可以看到,算法的思路就是“先拆分,再有序合并”
1、把大的数组平均拆分成两个子数组,并且子数组再递归地往下拆分,直到拆到最后子数组长度为1;
2、把数组长度为1的数组和相邻的数组长度为1的子数组组合成一个有序的父数组,然后递归地组合成更大的一个数组,直到数组长度等于原始数组长度。
代码实现(没写出来,糗大了)
写不出来就去看了网上归并排序的写法,发现其实也没那么难,但是自己写的时候感觉困难重重,哎,自己就是垃圾。
直接上网上的写法吧(参考文章“Java基础——递归实现归并排序和快速排序”)
算法描述:
-
把长度为n的输入序列分成两个长度为n/2的子序列;
-
对这两个子序列分别采用归并排序;
-
将两个排序好的子序列合并成一个最终的排序序列。
递归理解:
对整个数组进行排序 可以拆分成 对数组的左子数组排序,再对数组的右子数组排序,然后把左右子数组串成一个数组并排好序。假如用函数f(array)表示满足我们排序要求的函数,则
f(array)=merge(f(array[0~mid])+ f(array[mid + 1]~lenght - 1))
因为对左右子数组还需要再合并,所以假设有这么一个函数merge().
代码实现
/**
* 分解数组
* @param array
* @param left
* @param right
*/
public static void mergeSort(int array[],int left,int right){
if(left>=right){
return;
}
int mid = (left+right)>>>1;
mergeSort(array,left,mid);
mergeSort(array,mid+1,right);
merge(array,left,mid,right);//合并
}
/**
* 合并数组
* @param array
* @param left
* @param mid
* @param right
*/
public static void merge(int[] array,int left,int mid,int right){
int s1 = left;
int s2 = mid+1;
int [] res = new int[right-left+1];
int i=0;
while(s1 <= mid && right >=s2){
if(array[s1] <= array[s2]){
res[i++] = array[s1++];
}else {
res[i++] = array[s2++];
}
//res[i++]= array[s1] <= array[s2] ? array[s1++] :array[s2++];
}
while(s1 <= mid){
res[i++] = array[s1++];
}
while(s2 <= right){
res[i++] = array[s2++];
}
System.arraycopy(res,0,array,0+left,res.length);
}
1、mergeSort递归函数首先要设置递归结束条件,并且该条件必须在函数最上面if(left>=right);
2、自己当时在尝试写递归实现的时候,在思考mergeSort应该返回什么,参数应该怎么设置?其实数组参数是传址的,所以不需要函数返回一个数组,只需要保证调用函数后array中元素是有序的就行;尝试过mergeSort只有一个表示数组的参数,但发现这样很不方便,我要获取数组的左或右子数组的话,我还需要用一个临时数组把左右子数组存起来。
3、归并递归不是像二分查找那样的尾递归了,函数中执行调用自己的函数后还有逻辑要执行。