LeetCode 315. 计算右侧小于当前元素的个数
题目链接
题目分析
这个题让我们统计nums[i]的右边比nums[i]小的数字的个数。第一眼看我直接就暴力解了,解到15个测试用例TLE了,所以换思想。
留意到题目的tag带有二分查找,排序。我开始注意在我们暴力解的过程中,我们每次都需要在i+1 ~ nums.length-1中寻找比nums[i]小的最大的数,那么我们是不是可以把右手边的这些已经完成判定的数进行一轮排序,然后我们可以直接使用二分查找的方法找到比nums[i]小的最大的数呢。
事实上是可以的,我们先进行两个base case的处理,分别是nums.length等于0和1的时候。然后单独拿最后两个数进行一轮判断。
在这做这个方法的时候,我引用了Arrays.sort()的系统库来进行数组排序,后来发现依旧TLE,后来想到系统自带的排序是快排的改进,我们右手边的数组是属于相对有序的,如果再次进行快排的操作,时间复杂度会变成O(n^2),于是我把系统库方法去掉,使用插入排序的思想进行排序。后来勉强过了。
代码实现
class Solution {
public List<Integer> countSmaller(int[] nums) {
List<Integer> list = new LinkedList<>();
if(nums.length == 0){
return list;
}
list.add(0);
if(nums.length == 1){
return list;
}
if(nums[nums.length-2] > nums[nums.length - 1]){
list.add(0,1);
int temp = nums[nums.length-1];
nums[nums.length-1] = nums[nums.length-2];
nums[nums.length-2] = temp;
}else{
list.add(0,0);
}
for(int i = nums.length - 3; i >= 0; i--){
int left = i+1;
int right = nums.length-1;
while(left <= right){
int mid = left + ((right - left) >> 1);
if(nums[i] > nums[mid]){
left = mid + 1;
}else if(nums[i] < nums[mid]){
right = mid - 1;
}else{
while(mid >= left && nums[mid] == nums[i]){
mid--;
}
right = mid;
break;
}
}
int temp = nums[i];
for(int j = i; j < right; j++){
nums[j] = nums[j+1];
}
nums[right] = temp;
list.add(0,right - i);
}
return list;
}
}
总结
二分查找提醒变化万千,我这里做的效率也不高,我在做的时候也想过使用归并排序的思想来进行操作,但是没想到怎么很好的处理每个数据与其结果之间的映射,有点头疼。