Leetcode 315 Count of Small Numbers After Self
题目原文
You are given an integer array nums and you have to return a newcounts array.The counts array has the property where counts[i]
is the number of smaller elements to the right ofnums[i]
.
Example:
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]
.
题意分析
给定一个数组,返回它右边数字中比它小的数的个数。
解法分析
本题如果采用暴力解法需要时间为O(n^2),我采用两种方法降低其复杂度,第一种是归并排序,类似求逆序数的方法,在归并排序过程中计算每一个元素右边的比它小的元素个数。另一种是二叉搜索树,即从nums左边向右边依次建立二叉搜索树,在建树过程中记录每一个元素右边比它小的元素的个数,由于每个点右边的数先进入BST中,所以这样做每一个点只用搜索一个较小的二叉树。
- 归并排序
- 二叉搜索树
归并排序
在归并排序合并的过程中,假设左右两个子数组已经排序完成,此时可以将两个子数组用新的空间存储,合并后的结果放回原址,在比较的过程中,如果左边数组的元素所对应的数大于右边某一个元素所对应的数(数组中存的是原数组元素的下标),则左边数组接下来的所有元素所对应的res都加一,继续进行合并比较操作,最后得到排序好的数组,同时完成题目要求。C++代码如下:
class Solution {
private:
vector<int> count;
public:
void merge(vector<int> &sub,int begin,int end,vector<int> &nums){
if(begin==end)
return;
int mid=(begin+end)/2;
merge(sub,begin,mid,nums);
merge(sub,mid+1,end,nums);
vector<int> left;
vector<int> right;
int i=0,j=0;
int s,k;
for(k=begin;k<=end;k++){
if(k<=mid)
left.push_back(sub[k]);
else
right.push_back(sub[k]);
}
for(k=begin;k<=end;k++){
if(i==left.size()){
sub[k]=right[j];
j++;
continue;
}
if(j==right.size()){
sub[k]=left[i];
i++;
continue;
}
if(nums[left[i]]>nums[right[j]]){
sub[k]=right[j];
j++;
for(s=i;s<left.size();s++)
count[left[s]]++;
}
else{
sub[k]=left[i];
i++;
}
}
}
vector<int> countSmaller(vector<int>& nums) {
vector<int> sub;
int i;
if(nums.size()==0)
return count;
for(i=0;i<nums.size();i++)
count.push_back(0);
for(i=0;i<nums.size();i++)
sub.push_back(i);
merge(sub,0,nums.size()-1,nums);
return count;
}
};
注意归并排序不是原址排序,但是每次合并过程会将排序结果放回原址。
二叉搜索树
在二叉搜索树建树过程中,对于每一个即将加入的点,都需要从root开始判断大小,最终放到树的叶子节点,利用这个性质可以从nums的右边开始建立二叉搜索树,对于每个节点记录它的value以及它左子树目前拥有的节点数(表示目前在该元素右方且小于它的元素),一个元素右边比它小的元素令为preSum,C++代码如下:
class Solution {
public:
class TreeNode{
public:
int leftSon,val;
TreeNode *left,*right;
TreeNode(int l,int v):left(NULL),right(NULL),leftSon(l),val(v){}
};
TreeNode* insert(int val,int preSum,vector<int> &ret,TreeNode *root,int index){
if(root==NULL){//find a place to insert the node,this is the end of the recursion
root=new TreeNode(0,val);
ret[index]=preSum;//insert the node,and its preSum is decided
}
else if(root->val>val){
root->leftSon++;//the node root have another leftSon,but it won't affect the preSum of root,because this node
//appears after the node root
root->left=insert(val,preSum,ret,root->left,index);
}
else{
root->right=insert(val,preSum+root->leftSon+((val>(root->val))?1:0),ret,root->right,index);
}
return root;//renew the root
}
vector<int> countSmaller(vector<int>& nums) {
vector<int> ff;
if(nums.size()==0)
return ff;
vector<int> ret(nums.size(),0);
TreeNode *root=NULL;
for(int i=nums.size()-1;i>=0;i--)//build the BST
root=insert(nums[i],0,ret,root,i);//renew the root
return ret;
}
};
insert返回一个节点指针,用于更新输入的根节点,因为它的子树已经添加了一个新的节点。insert的递归返回就是当root==NULL时,可以将nums[i]对应元素插入相应位置。preSum记录了一个待插入节点从root开始比较后得到的比它小的元素的个数(如果该点大于一个节点的value,则一定大于这个节点的左子树的所有元素,所以要对每一个节点记录leftSon),对于相等的情况不需要单独提出,只需要用一个?:语句。