315. Count of Smaller Numbers After Self

You are given an integer array nums and you have to return a new counts array. The counts array has the property wherecounts[i]is the number of smaller elements to the right of nums[i].
Example:

1
2
3
4
5
Given nums = [5, 2, 6, 1]
To the right of 5 there are 2 smaller elements (2 and 1).
To the right of 2 there is only 1 smaller element (1).
To the right of 6 there is 1 smaller element (1).
To the right of 1 there is 0 smaller element.

Return the array[2, 1, 1, 0].

[leetcode 315] Count of Smaller Numbers After Self

之前做笔试题的时候见到过这道题,我的解法就是最直接的,对每一个元素,每次遍历它后面的元素,找到比它小的元素个数,然后超时了,显然这种最直接的方法对于笔试是应该摒弃的。。。
下面给出discuss上的两种解法,分别使用二叉搜索树和归并排序实现,特别的好。
二叉搜索树
这个是排名第一的答案,遍历给定数组的过程就是建立一棵二叉搜索树的过程,一个节点有这么几个属性:左右节点,节点值,节点值相同的元素个数,节点左子树的个数。
对当前遍历的元素(从后往前),如下插入:从根节点出发,如果小于根节点,就直接转到根节点的左子树,如果等于根节点,将根节点的相同元素个数++,该元素对应的count[i]为节点左子树个数加先前的元素个数,如果大于,转到根节点右子树,并将节点的左子树的个数传入。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class BSTNode{
int lSum,dup=1,val;//java中不能在全局变量中赋值
BSTNode left,right;
public BSTNode(int lSum,int val){
this.lSum=lSum;
this.val=val;
}
}
public List<Integer> countSmaller(int[] nums) {
Integer res[]=new Integer[nums.length];
BSTNode root=null;//=new BSTNode(0,1);
for(int i=nums.length-1;i>=0;i--){
root=insert(root,nums[i],res,i,0);
}
return Arrays.asList(res);//对象数组转list
}
private BSTNode insert(BSTNode root, int num, Integer[] res, int i, int preSum) {
if(root==null){
root=new BSTNode(0,num);
res[i]=preSum;
}else if(root.val==num){
root.dup++;
res[i]=preSum+root.lSum;
}else if(root.val<num){
root.right=insert(root.right,num,res,i,preSum+root.lSum+root.dup);
}else {
root.lSum++;
root.left=insert(root.left,num,res,i,preSum);
}
return root;
}

归并排序
这个比较好理解,只要知道归并排序的思想,然后在排序的过程中,记录右边小于左边的个数,并叠加到左边,右边不变,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
int[] count;
public List<Integer> countSmaller(int[] nums) {
List<Integer> res = new ArrayList<Integer>();
count = new int[nums.length];
int[] indexes = new int[nums.length];
for(int i = 0; i < nums.length; i++){
indexes[i] = i;
}
mergesort(nums, indexes, 0, nums.length - 1);
for(int i = 0; i < count.length; i++){
res.add(count[i]);
}
return res;
}
private void mergesort(int[] nums, int[] indexes, int start, int end){
if(end <= start){
return;
}
int mid = (start + end) / 2;
mergesort(nums, indexes, start, mid);
mergesort(nums, indexes, mid + 1, end);
merge(nums, indexes, start, end);
}
private void merge(int[] nums, int[] indexes, int start, int end){
int mid = (start + end) / 2;
int left_index = start;
int right_index = mid+1;
int rightcount = 0;
int[] new_indexes = new int[end - start + 1];
int sort_index = 0;
while(left_index <= mid && right_index <= end){
if(nums[indexes[right_index]] < nums[indexes[left_index]]){
new_indexes[sort_index] = indexes[right_index];
rightcount++;
right_index++;
}else{
new_indexes[sort_index] = indexes[left_index];
count[indexes[left_index]] += rightcount;
left_index++;
}
sort_index++;
}
while(left_index <= mid){
new_indexes[sort_index] = indexes[left_index];
count[indexes[left_index]] += rightcount;
left_index++;
sort_index++;
}
while(right_index <= end){
new_indexes[sort_index++] = indexes[right_index++];
}
for(int i = start; i <= end; i++){
indexes[i] = new_indexes[i - start];
}
}

这里我还是举个例子吧(交换值而不是代码中所示的交换索引),比如[5,2,6,1],分解后变为5261,然后是5和2归并,第一个位置,2小于5,那么将count++,但是2对应的count[1]=0不变,第二个位置,将5添加,并将5对应的count[0]和count相加赋给count[0],count[0]=1,同理对于6和1,count[2]=1,count[3]=0;然后继续归并,2,51,6,第一个位置是1,count=1,第二个位置是2,count[1]=count[1]+count=1,第三个位置是5,count[0]=count[0]+count=2,第4个位置是6,count[2]=count[2],归并完成,这样对应的count数组就是:[2,1,1,0]

如果觉得有帮助,给我打赏吧!