315. 计算右侧小于当前元素的个数(插入排序+二分查找 与 线段树两种方法)
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i]
的值是 nums[i]
右侧小于 nums[i]
的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.
我用的方法是,从后往前一边进行二分查找,一边进行从大到小的插入排序。保证右面序列是有序的。找到的插入位置右面元素的数量就是所求值。
代码如下:
class Solution {
public List<Integer> countSmaller(int[] nums) {
int len = nums.length;
List<Integer> counts = new ArrayList<>();
if(len == 0)
return counts;
counts.add(0);
for(int i = len-2 ; i >= 0 ; i--){
int left = i + 1;
int right = len;
while(left < right){
int mid = (left + right) / 2;
if(nums[i] > nums[mid])
right = mid;
else
left = mid + 1;
}
counts.add(len - left);
int temp = nums[i];
for(int j = i ; j < left - 1 ; j++)
nums[j] = nums[j+1];
nums[left-1] = temp;
}
Collections.reverse(counts);
return counts;
}
}
看了一下大家的题解,是可以用线段树或者树状数组的。所以参考大佬的代码写了一个java版本的线段树解法。时间上能优化不少。
public class TreeNode{
int start;
int end;
int val;
TreeNode left;
TreeNode right;
TreeNode(int s , int e){
val = 0;
left = null;
right = null;
start = s;
end = e;
}
}
class Solution {
public List<Integer> countSmaller(int[] nums) {
List<Integer> list = new ArrayList<>();
int len = nums.length;
if(len == 0)
return list;
int start = nums[0];
int end = nums[0];
//找最大最小值
for (int i = 1; i < len ; i++ ){
start = Math.min(start, nums[i]);
end = Math.max(end, nums[i]);
}
TreeNode root = bulid(start,end);
for (int i = len-1 ; i >= 0 ; i--){
list.add(check(root, start, nums[i]-1));
insert(root, nums[i]);
}
Collections.reverse(list);
return list;
}
//建树
public TreeNode bulid(int start , int end){
if(start > end)
return null;
TreeNode root = new TreeNode(start,end);
if (start == end)
root.val = 0;
else{
int mid = start+ (end-start)/2;
root.left = bulid(start,mid);
root.right = bulid(mid+1,end);
}
return root;
}
//查询
public int check(TreeNode root,int start,int end){
if(root == null || start >end)
return 0;
if(start == root.start && end == root.end)
return root.val;
int mid = root.start + (root.end - root.start)/2;
int leftcount = 0 ,rightcount = 0;
if (start <= mid){
if (mid < end)
leftcount = check(root.left, start, mid);
else
leftcount = check(root.left, start, end);
}
if (mid < end){
if (start <= mid)
rightcount = check(root.right, mid+1, end);
else
rightcount = check(root.right, start, end);
}
return (leftcount + rightcount);
}
//更新树
public void insert(TreeNode root,int index){
if (root.start==index && root.end==index){
root.val += 1;
return;
}
int mid = root.start + (root.end - root.start)/2;
if (index>=root.start && index<=mid){
insert(root.left, index);
}
if (index>mid && index<=root.end){
insert(root.right, index);
}
root.val = root.left.val + root.right.val;
}
}