1. 题目描述
给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例 1:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
2. 思路/题解
2.1 思考核心
💡 如何统计每一个元素的右边到底有多少个元素比自己小
2.2 方法步骤
概述
将原数组构建成带有索引的二维数组,在降序归并排序的合并过程中维护结果数组
做本题之前可以先复习一下归并排序,然后做一下 LCR 170.交易逆序对的总数 ,LCR 170 相对简单一些,它是在归并排序的合并过程中维护「结果变量」,而本题要维护的则是「结果数组」
重点
-
由于在合并过程中需要不断维护结果数组,这就需要利用好索引,因此可以在排序前先将原输入数组构建成带有索引的二维数组。
# 将原输入数组构建成带有索引的数组 numsarr = [[nums[i], i] for i in range(n)]
-
统计 nums[i] 的右侧有多少小于它的元素👉在降序归并排序的合并过程中,如果发现 nums[i] > nums[j] , 由于 nums[j...right] 已经降序排列好,那显然 nums[j...right] 都是小于 nums[i] 且位于 nums[i] 右侧的元素,一共有 right - j + 1 个,它们都应被更新到「结果数组」中。
下面用一张图来说明红字内容
2.3 具体代码
class Solution:
def __init__(self):
self.tmp = []
def countSmaller(self, nums: List[int]) -> List[int]:
n = len(nums)
numsarr = [[nums[i], i] for i in range(n)]
res = [0] * n
self.tmp = [[nums[i],i] for i in range(n)]
def mergeSort(left, right):
if left >= right:
return
mid = (left + right) // 2
mergeSort(left, mid)
mergeSort(mid + 1, right)
# 归并排序需要额外的空间
self.tmp[left : right + 1] = numsarr[left : right+1]
i , j , k = left, mid +1, left
# 因为是降序排列,始终将比较下来更大的元素拿下来
while i <= mid and j <= right:
if self.tmp[j][0] >= self.tmp[i][0]:
numsarr[k] = self.tmp[j]
j += 1
else:
numsarr[k] = self.tmp[i]
i += 1
# 更新结果值, 因为此时 tmp[i][0] 的值比 tmp[j:right+1][0] 的值都大
res[numsarr[k][1]] += right - j + 1
k += 1
# 说明右边的一半走完了,左边剩下的都是小于右边的,直接填进去就好
if i <= mid:
numsarr[k: right+1] = self.tmp[i:mid+1]
else:
numsarr[k: right+1] = self.tmp[j:right+1]
mergeSort(0,n-1)
return res