[LeetCode] 315. 计算右侧小于当前元素的个数

23 阅读2分钟

315. 计算右侧小于当前元素的个数

Hard

方法一(暴力)(TLE)

思路

首先想到的方法是暴力解,题目没有给出数据规模。但是作为Hard题,暴力解大概率会超时。

尝试一下,果不其然,TLE

代码

python3

class Solution:
  def countSmaller(self, nums: List[int]) -> List[int]:
    r = []
    for i in range(len(nums)):
      t = 0
      for j in range(i, len(nums)):
        if nums[j] < nums[i]:
          t += 1
      r.append(t)
    return r

方法二(归并排序)

阅读大佬解法,获得新思路,使用归并排序求解

这道题本质上说是求逆序数,而逆序数归并排序的副产物

话不多说,先学一波归并排序:

www.interviewbit.com/tutorial/me… www.jianshu.com/p/3ad537346… www.cnblogs.com/chengxiao/p…

学习了归并排序之后再回过头看这道题

  • 逆序数之所以用归并排序来优化,是因为在归并的的过程中,我们是局部有序的,这样我们就可以直接获得这段中的逆序数
  • 但是中间这部分的逆序数不是完整的,在合并的过程中,数的位置会发生改变。统计的逆序数也会发生改变
  • 为了解决上面的问题,我们需要一个数组indexes用来记录所有元素的索引
  • indexes的作用是,在排序的过程中,我们交换的不是数字,是下标。只是交换的时候比较的是原来的数字
  • 也就是说,我们使用nums[indexes[i]]nums[indexes[j]]的比较结果来决定indexes[i]indexes[j]的先后顺序

补充说明

  • 刚接触这题,索引数组的使用想了很久,这部分还是有点绕的
  • 首先我们为什么要使用索引数组?因为,如果我们直接用MergeSort的话,数字的位置是会发生改变的。当我们求出了某个数右面的逆序数的话,我们无法定位他在解中的位置。所以我们要定义一个索引数组,来做一个映射
  • 要注意的是当合并操作时,当right中的当前数比left的数小是,我们需要使用一个计数变量记录一下,right后面的数都要加上这个计数,因为right中是单调的,后面的数比前面的大,如果一个数比前面的数小,那必然比后面的小

以下视屏,讲的很清晰,看两遍就能理解了

参考 happygrilzt

LeetCode 315 Count of Smaller Numbers After Self 中文解释 Chinese Version

以上,尝试写一下代码,AC!

代码

python3

class Solution:
  def merge(self,nums, indexes, left, right, res):
    i = 0
    j = 0
    r = []
    rightCount = 0
    while i < len(left) and j < len(right):
      if nums[left[i]] <= nums[right[j]]:
        res[left[i]] += rightCount
        r.append(left[i])
        i += 1
      else:
        rightCount += 1
        r.append(right[j])
        j += 1
    while i < len(left):
      res[left[i]] += rightCount
      r.append(left[i])
      i += 1
    while j < len(right):
      r.append(right[j])
      j += 1
    return r

  def mergeSort(self,nums, indexes, start ,end, res):
    if end-start <= 1:
      return indexes[start:end]
    mid = start + (end-start) // 2
    left = self.mergeSort(nums, indexes, start ,mid, res)
    right = self.mergeSort(nums, indexes, mid , end, res)
    return self.merge(nums,indexes,left,right, res)

  def countSmaller(self, nums: List[int]) -> List[int]:
    indexes = [ i for i in range(len(nums)) ]
    res = [ 0 for i in range(len(nums)) ]
    r = self.mergeSort(nums,indexes, 0,len(nums), res) 
    return res