LeetCode-Python-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 个更小的元素.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

第一种思路:

双重循环暴力解。

会超时。

时间复杂度:O(N^2)

空间复杂度:O(1)

class Solution(object):
    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        
        counts = [0 for _ in range(len(nums))]
        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                if nums[i] > nums[j]:
                    counts[i] += 1
                
        return counts

第二种思路:

二分查找。

如果我们从左向右处理nums,则因为每个数的右边都是未知的,所以非常麻烦不好弄,

但如果我们从右往左处理nums,在处理每个数的时候,其右边的所有数我们都已经处理过,所以都已知,

因此可以维护一个processed_num的有序数组,里面存放了所有的从右往左已经见过的数,但是是有序的形式,

这样我们只要使用二分查找找到当前数,在processed_num对应的插入位置,就可以知道其右边有多少个数比它的值要小。

时间复杂度:O(N (logN + N)) = O(N^2)

空间复杂度:O(N)

非常神奇的一点在于,虽然这个算法也是平方级的时间复杂度,但它跑的比很多O(NlogN)的算法还要快……

原因据说是因为bisect这个库是用 c 实现的,所以跑起来飞快……

详细解释可以看:https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76635/Python-O(n2)-faster-than-O(n-log-n)

class Solution(object):
    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        
        # 从左往右处理,右边是未知的,所以不好弄
        # 从右往左处理,则对于每个数,其右边的数都已知
        processed_num = []
        res = []
        for num in nums[::-1]:
            idx = bisect.bisect_left(processed_num, num)
            res.append(idx)
            processed_num.insert(idx, num)
        return res[::-1]
            

第三种思路:

BST,

我们可以构建并维护一种特殊的二叉搜索树,

除了常规的left, right 和 val之外,再额外多一项属性 left_subtree_cnt 代表当前节点的左子树的节点总数。

这样对于二叉搜索树的每个节点,读它的 left_subtree_cnt 就可以很快知道有多少个数比它要小。

时间复杂度:O(NlogN)

空间复杂度:O(N)

class TreeNode(object):
    def __init__(self, val):
        self.left = None
        self.right = None
        self.val = val
        self.left_subtree_cnt = 0
        
class Solution(object):
    def countSmaller(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        # 从左往右处理,右边是未知的,所以不好弄
        # 从右往左处理,则对于每个数,其右边的数都已知
        res = [0 for _ in nums]
        root = None
        for i, num in enumerate(nums[::-1]):
            root = self.insert(root, num, i, res)
        return res[::-1]
    
    def insert(self, root, val, i, res):
        if not root: #如果当前root为空
            root = TreeNode(val)
        elif root.val >= val: # 如果应该插入左子树
            root.left_subtree_cnt += 1
            root.left = self.insert(root.left, val, i, res)
        elif root.val < val: # 如果应该插入右子树
            res[i] += root.left_subtree_cnt + 1 # 1 代表当前root
            root.right = self.insert(root.right, val, i, res)
            
        return root
            
            

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值