思路:可以看作是查找逆序对的个数。通过归并排序,可以在比较两个数大小的时候知道逆序对的个数,但是如果真的排序,会破坏掉原本数组的顺序,对后续的查找逆序对有影响,因此选择创建一个数组存放下标,每次排序都是对下标排序。
public List<Integer> countSmaller(int[] nums){
List<Integer> list=new ArrayList<>();
int len=nums.length;
if(len==0){
return list;
}
int[] res=new int[len];
int[] temp=new int[len];
int[] indexes=new int[len];
for(int i=0;i<len;i++){
indexes[i]=i;
}
mergeAndCountSmaller(0,len-1,nums,res,temp,indexes);
for(int i=0;i<len;i++){
list.add(res[i]);
}
return list;
}
private void mergeAndCountSmaller(int left,int right,int[] nums,int[] res,int[] temp,int[] indexes){
if(left>=right) return;
int mid=left+(right-left)/2;
mergeAndCountSmaller(left,mid,nums,res,temp,indexes);
mergeAndCountSmaller(mid+1,right,nums,res,temp,indexes);
//优化归并排序
if(nums[indexes[mid]]<=nums[indexes[mid+1]]){
return;
}
mergeOfTwoSortedArrAndCountSmaller(left,mid,right,nums,res,temp,indexes);
}
private void mergeOfTwoSortedArrAndCountSmaller(int left,int mid,int right,int[] nums,int[] res,int[] temp,int[] indexes){
for(int i=left;i<=right;i++){
temp[i]=indexes[i];
}
int l=left;
int r=mid+1;
for(int k=left;k<=right;k++){
if(l>mid){
indexes[k]=temp[r++];
}
else if(r>right){
res[temp[l]]+=(right-mid);
indexes[k]=temp[l++];
}
else if(nums[temp[l]]<=nums[temp[r]]){
res[temp[l]]+=(r-mid-1);
indexes[k]=temp[l++];
}
else {
indexes[k]=temp[r++];
}
}
}