难度:Hard
题目:
给你一个整数数组
nums
,按要求返回一个新数组counts
。数组counts
有该性质:counts[i]
的值是nums[i]
右侧小于nums[i]
的元素的数量。
示例 1:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
示例 2:
输入:nums = [-1] 输出:[0]
示例 3:
输入:nums = [-1,-1] 输出:[0,0]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
Related Topics
- 树状数组
- 线段树
- 数组
- 二分查找
- 分治
- 有序集合
- 归并排序
重点!!!解题思路
明确解题思路:
题中要求求出右侧小于当前元素的数量,如果将数组拆分开来,左右两边都是降序排列,如果当左边数组有一个值大于右边数组的其中一个值,那么就意味着比当前左边这个数小的元素有右边那个数到右边数组末尾那么多个,即题目可使用归并排序来解决。
源码+讲解:
class Solution {
class Data { //因为你要记录每个值所对应得右边元素个数,hashmap太麻烦,我们直接封装一个内部类
int ind, val, cnt; //对应下标,值,右边对应得元素
public Data(int ind, int val) { //初始化
this.ind = ind;
this.val = val;
cnt = 0;
}
}
Data[] temp; //归并排序的克隆数组
public List<Integer> countSmaller(int[] nums) {
temp = new Data[nums.length];
Data[] data = new Data[nums.length]; //待排序数组
for (int i = 0; i < nums.length; i++) { //将每个元素封装到类中
data[i] = new Data(i, nums[i]);
}
merge_sort(data, 0, data.length - 1); //开始归并排序
Arrays.sort(data, new Comparator<Data>() { //当归并排序后,我们需要每个数组下标从小到大来输出,那么我们就需要一个小顶堆
@Override
public int compare(Data o1, Data o2) {
return o1.ind - o2.ind;
}
});
List<Integer> res = new ArrayList<>(); //集合结果用来还原堆中的值
for (Data datum : data) {
res.add(datum.cnt);
}
return res;
}
//很基础的一个归并排序解法 如果有不懂得朋友 可以看看我前面发的题
public void merge_sort(Data[] data, int l, int r) {
if (l >= r) return;
int mid = (l + r) >> 1;
merge_sort(data, l, mid);
merge_sort(data, mid + 1, r);
int k = l, p1 = l, p2 = mid + 1;
while (p1 <= mid || p2 <= r) {
if (p2 > r || (p1 <= mid && data[p1].val > data[p2].val)) {
data[p1].cnt+=(r-p2+1);
temp[k++] = data[p1++];
} else {
temp[k++] = data[p2++];
}
}
for (int i=l;i<=r;i++) data[i]=temp[i];
}
}
运行结果:
如果您还有什么疑问或解答有问题,可在下方评论,我会及时回复。
系列持续更新中,点个订阅吧,喜欢练习算法那就点个攒吧