美文网首页
2020-07-11 leetcode 计算右侧小于当前元素的个

2020-07-11 leetcode 计算右侧小于当前元素的个

作者: XaviSong | 来源:发表于2020-07-11 23:26 被阅读0次

给定一个整数数组 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 个更小的元素.

解题思路:

首先尝试暴力法,一般来说最后肯定会超时的,但是试试呗

class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        n = len(nums)
        count = [0] * n
        index = 0
        for value in nums:
            for i in range(index + 1,n):
                if nums[i] < value:
                    count[index] += 1
            index += 1
        return count

果然超时,那么我们就想办法优化这个O(n2)的算法,不考虑别的思路的情况下,我们来看这个O(n2)具体干了什么。最外层循环是遍历nums数组,显然这个应该是不好优化的。内层循环是直接统计右边的数组元素计数,这里就可以稍微改动一下。事实上如果我们有一个升序的有序数组,那么我们直接返回下标应该就是我们想要的count值。

[1,2,3,4,5,6,7]
[0,1,2,3,4,5,6]

所以我们的目标就改为给原数组排序,排序时由于有重复的数字情况出现,我们希望新插入的元素可以每次插到相同元素序列的左侧,这样所有的相同元素都会有同一个count。使用bisect模块提供的二分实现bisect_left。

def bisect_left(a, x, lo=0, hi=None):
    '''
    a 原列表
    x 插入的元素
    lo 起始位置 默认值为0
    hi 结束位置 默认值为len(a)
    目的: 查找该数值将会插入的位置并返回,而不会插入。如果x存在在a中则返回x左边的位置。
    '''
    # 如果起始位置小于0 则报错
    if lo < 0:
        raise ValueError('lo must be non-negative')
    # 如果没有 结束位置 则 默认为 列表的长度
    if hi is None:
        hi = len(a)
    # 二分法
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    # 仅返回位置
    return lo

题目的要求与仅仅排序还有一定区别,因为每次仅仅与右侧元素相比,所以向a列表中放元素时,从后往前放,才能得到正确答案

完整代码:

class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        '''
        sort_nums中是一直升序有序的
        该算法时间复杂度为O(nlogn)
        '''
        n = len(nums)
        count = [0] * n
        sort_nums = []
        for i in range(n-1,-1,-1):
            index = bisect.bisect_left(sort_nums,nums[i])
            sort_nums.insert(index,nums[i])
            count[i] = index
        return count

bisect模块的其他函数:

1、insort_left(a, x, lo=0, hi=None)

def insort_left(a, x, lo=0, hi=None):
    '''
    a 原列表
    x 插入的元素
    lo 起始位置 默认值为0
    hi 结束位置 默认值为len(a)
    目的:在列表a中插入元素x,并在排序后保持排序。如果x已经在a中,把它插入右x的左边。
    '''
    # 如果起始位置小于0 则报错
    if lo < 0:
        raise ValueError('lo must be non-negative')
    # 如果没有 结束位置 则 默认为 列表的长度
    if hi is None:
        hi = len(a)
    # 二分查找
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    # 插入
    a.insert(lo, x)

2、insort_right(a, x, lo=0, hi=None)

def insort_right(a, x, lo=0, hi=None):
    '''
    a 原列表
    x 插入的元素
    lo 起始位置 默认值为0
    hi 结束位置 默认值为len(a)
    目的:在列表a中插入元素x,并在排序后保持排序。如果x已经在a中,把它插入右x的右边。
    '''
    # 如果起始位置小于0 则报错
    if lo < 0:
        raise ValueError('lo must be non-negative')
    # 如果没有 结束位置 则 默认为 列表的长度
    if hi is None:
        hi = len(a)
    # 二分法查找 
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    # 插入
    a.insert(lo, x)

3、bisect_right(a, x, lo=0, hi=None)

def bisect_right(a, x, lo=0, hi=None):
    '''
    a 原列表
    x 插入的元素
    lo 起始位置 默认值为0
    hi 结束位置 默认值为len(a)
    目的:其目的在于查找该数值将会插入的位置并返回,而不会插入。如果x存在在a中则返回x右边的位置
    '''
    # 如果起始位置小于0 则报错
    if lo < 0:
        raise ValueError('lo must be non-negative')
    # 如果没有 结束位置 则 默认为 列表的长度
    if hi is None:
        hi = len(a)
    # 二分法
    while lo < hi:
        mid = (lo+hi)//2
        if x < a[mid]: hi = mid
        else: lo = mid+1
    # 仅返回位置
    return lo

还要学习的其他解法:

  1. 归并排序
  2. BST
  3. 树状数组
  4. 线段树

相关文章

网友评论

      本文标题:2020-07-11 leetcode 计算右侧小于当前元素的个

      本文链接:https://www.haomeiwen.com/subject/bwvwcktx.html