小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
315. 计算右侧小于当前元素的个数
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
解题思路
使用归并排序,每一次合并的时候,对于左区间的每个元素,根据右区间指针的位置我们可以得出,右区间存在多少个大于当前元素的(即在当前元素前已经被合并的元素个数)。
使用一个额外的index数组,记录下每个元素在原数组的下标,使得我们可以将对应的结果加入到答案数组中
代码
class Solution {
int[] idx;
int[] res;
public List<Integer> countSmaller(int[] nums) {
int n=nums.length;
idx=new int[n];
res=new int[n];
for(int i=0;i<n;i++)
idx[i]=i;
List<Integer> list=new ArrayList<>();
mergeSort(nums,0,n-1);
for(int i=0;i<n;i++)
list.add(res[i]);
return list;
}
public void mergeSort(int[] nums,int l,int r)
{
if(l<r)
{
int mid=(r-l)/2+l;
mergeSort(nums,l,mid);
mergeSort(nums,mid+1,r);
merge(nums,l,mid,r);
}
}
public void merge(int[] nums,int l,int mid,int r)
{
int i=l,j=mid+1,p=0;
int[] t=new int[r-l+1];
int[] ni=new int[r-l+1];
while(i<=mid&&j<=r)
{
if(nums[i]<=nums[j])
{
res[idx[i]]+=(j-mid-1);
t[p]=nums[i];
ni[p]=idx[i];
p++;
i++;
}else {
t[p]=nums[j];
ni[p]=idx[j];
p++;
j++;
}
}
while(i<=mid)
{
res[idx[i]]+=(j-mid-1);
t[p]=nums[i];
ni[p]=idx[i];
p++;
i++;
}
while(j<=r)
{
t[p]=nums[j];
ni[p]=idx[j];
p++;
j++;
}
for(int k=0;k<r-l+1;k++)
{
idx[k+l]=ni[k];
nums[k+l]=t[k];
}
}
}