Leetcode算法学习日志-315 Count of Small Numbers After Self

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),对于相等的情况不需要单独提出,只需要用一个?:语句。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值