给定一个整数数组 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