一开始我以为两个数要求是相邻的,后来看了书才发现不一定是相邻的,实际上连题都没审清楚,
前面一个大于后面的数字,逆序对
例子中的逆序对有 1 0 ,2 0, 3 0,4 0,5 0,6 0,7 0
思路一:暴力破解双循环 时间O(N^2),复杂度过高,值得注意的是,取余的操作需要在累加的过程中实现,防止累加值溢出,这也是题目为什么要求返回值取余
public class Solution {
public int InversePairs(int [] array) {
int num=0;
for(int i=0;i<array.length;i++)
{
for(int j=i+1;j<array.length;j++)
{
if(array[i]>array[j])
{
num++;
num%=1000000007;
}
}
}
return num;
}
}
从前往后依次遍历的顺序行不通,那么我们能否从相邻的位置开始
思路二:归并排序
临时数组存在的意义
这里我举个例子,比如需要合并的两个有序区间为[3 4] 和 [1 2]
我们需要得到最后的结果为[1 2 3 4], 如果不需要额外的空间的话,是做不到的,
当比较1 和 3 的时候, 1 比 3 小,就会覆盖原来的位置。
正常的归并排序如下:
public class Solution {
private int count=0;
public int InversePairs(int [] array) {
if(array==null||array.length==0)
return 0;
int[] tempList =new int[array.length];
mergeSort(array,0,array.length-1,tempList);
}
//递归调用函数
private void mergeSort(int[] arrs,int left,int right,int[] tempList)
{
if(left>=right)//只有一个元素
return;
int mid=left+(right-left)/2;
mergeSort(arrs,left,mid,tempList);
mergeSort(arrs,mid+1,right,tempList);
merge(arrs,left,right,mid,tempList);
}
//合并
private void merge(int arrs[],int left ,int right,int mid,int[] tempList)
{
int i=left;
int j=right;
int t=0;
//元素按序存入临时数组
while(i<=left&&j<=right)
{
if(arrs[i]<arrs[j])
{
tempList[t++]=arrs[i++];
}
else
{
tempList[t++]=arrs[j++];
}
}
//剩余数组压入
while(i<=left)
{
tempList[t++]=arrs[i++];
}
while(j<=right)
{
tempList[t++]=arrs[j++];
}
//数组拷贝
t=0;
for(int k=left;k<right;k++)
{
arrs[k]=arrs[t++];
}
}
}
本题的归并:
public class Solution {
private int count=0;
public int InversePairs(int [] array) {
if(array==null||array.length==0)
return 0;
int[] tempList =new int[array.length];
mergeSort(array,0,array.length-1,tempList);
return count;
}
private void mergeSort(int[] arrs,int left,int right,int[] tempList)
{
if(left>=right)//只有一个元素
return;
int mid=left+(right-left)/2;
mergeSort(arrs,left,mid,tempList);
mergeSort(arrs,mid+1,right,tempList);
merge(arrs,left,right,mid,tempList);
}
//合并
private void merge(int arrs[],int left ,int right,int mid,int[] tempList)
{
int i=mid;//左边最左端
int j=right;//右边最左端
int t=right;//临时数组尾索引
while(i>=left&&j>=mid+1)//:两边从后往前排
{
if(arrs[i]>arrs[j])
{
tempList[t--]=arrs[i--];
count=(count+j-mid)%1000000007;
}
else
{
tempList[t--]=arrs[j--];
}
}
//剩余数组压入
while(i>=left)
{
tempList[t--]=arrs[i--];
}
while(j>=mid+1)
{
tempList[t--]=arrs[j--];
}
//数组拷贝
for(int k=right;k>=left;k--)
{
arrs[k]=tempList[k];
}
}
}
注意点:
本题临时数组是从后(right)往前压入的,每一次并的数组的范围left到right,拷贝的时候注意临时数组的边界【right到left】边界不是0,和索引0没关系0【这个地方卡半天】
正常的归并排序是压入临时数组是从索引0开始,所以拷贝从临时数组的索引0开始,但原数组相应的位置依然是left到right
时间O(nlog) 空间O(N) 空间换时间
思路看书,有序性的作用