题目
思路
(1)暴力求解
由于数组长度限制为0-50000,两层循环最终会导致超时,因此不可取
(2)归并排序
归并排序分为分-治两阶段,统计逆序对是在治的阶段,即合并过程中统计的。
合并阶段 本质上是 合并两个排序数组
的过程,而每当遇到 左子数组当前元素 > 右子数组当前元素
时,意味着 「左子数组当前元素 至 末尾元素」
与 「右子数组当前元素」
构成了若干 「逆序对」
。
例如当前需要归并排序的数组为[7,5,6,4]
第一次合并的两数组为[7]
,[5]
①7与5比较
由于7>5
,因此 「左子数组当前元素 至 末尾元素」
与 「右子数组当前元素」
构成了若干 「逆序对」
,即res=0+1=1
第二次合并的两数组为[6]
,[4]
①6与4比较
由于6>4
,因此 「左子数组当前元素 至 末尾元素」
与 「右子数组当前元素」
构成了若干 「逆序对」
,即res=1+1=2
那么在最后一次合并的两数组为[5,7]
,[4,6]
①5与4比较
由于5>4
,因此 「左子数组当前元素 至 末尾元素」
与 「右子数组当前元素」
构成了若干 「逆序对」
,即res=2+2=4
②5与6比较
无逆序对
③7与6比较
由于7>6
,因此 「左子数组当前元素 至 末尾元素」
与 「右子数组当前元素」
构成了若干 「逆序对」
,即res=4+1=5
代码
class Solution {
int res=0;
public int reversePairs(int[] nums) {
int temp[]=new int[nums.length];
mergeSort(nums,0,nums.length-1,temp);
return res;
}
//arr是待排序数组
//left是数组起始位置
//right是数组结束位置
//temp是中间治的时候的中转数组
public void mergeSort(int arr[],int left,int right,int temp[])
{
int mid=(left+right)/2;
if(left<right)
{
//分
mergeSort(arr,left,mid,temp);
mergeSort(arr,mid+1,right,temp);
//治
merge(arr,left,mid,right,temp);
}
}
/*
arr是待排序数组
left是左边数组的起始位置
mid是左边数组的结束位置
那么mid+1就是右边数组的起始位置
right是右边数组的结束位置
//temp是中间治的时候的中转数组
*/
public void merge(int arr[],int left,int mid,int right,int temp[])
{
int i=left;
int j=mid+1;
int t=0;
while(i<=mid && j<=right)
{
if(arr[i]>arr[j])
{
temp[t++]=arr[j++];
res+=mid-i+1;
}
else
{
temp[t++]=arr[i++];
}
}
while(i<=mid)
{
temp[t++]=arr[i++];
}
while(j<=right)
{
temp[t++]=arr[j++];
}
//腾回到原来的数组
t=0;
int k=left;
while(k<=right)
{
arr[k++]=temp[t++];
}
}
}