315.计算右侧小于当前元素的个数
给你一个整数数组
nums
,按要求返回一个新数组counts
。数组counts
有该性质:counts[i]
的值是nums[i]
右侧小于nums[i]
的元素的数量。示例1:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]示例2:
输入:[-1]
输出:[0]示例3:
输入:[-1,-1]
输出:[0,0]提示:
- 1 < = n u m s . l e n g t h < = 1 0 5 1<=nums.length<=10^5 1<=nums.length<=105
- − 1 0 4 < = n u m s [ i ] < = 1 0 4 -10^4<=nums[i]<=10^4 −104<=nums[i]<=104
超时尝试
1、暴力搜索
最简单直观的方法就是对每一个元素进行遍历搜索。但该题的难度标注为困难,使用暴力搜索一定会导致超时。尝试结果:超出时间限制。
2、逆序遍历,记录各元素前一次的索引
对暴力搜索进行优化:假设一个元素x出现了多次,其中x1和x2(x1<x2)是其两次出现的下标。进行逆序遍历,当遍历到x1时,x2一定已经得到正确的count2,此时只需要统计(x1,x2)之间小于x的元素个数count(x1,x2),在加上count2,就能得到x1右侧小于x的元素个数count1。
对于存在大量重复元素的数组,该方法应该能取得较好的效果,但由于该题给出的元素范围为
[
−
1
0
4
,
1
0
4
]
[-10^4,10^4]
[−104,104],元素重复的概率仍然很小,该方法效果可能相较暴力搜索提升不大,在数组无重复元素时等同于暴力搜索。尝试结果:超出时间限制。
对索引x1,x2,如果nums[x1]=nums[x2],显然counts[x1]=counts[x2]+count(x1,x2);但当nums[x1]<>nums[x2]时,counts[x1]与counts[x2]之间缺少一种明确的关系,因此动态规划算法不适合用于解决该问题。
3、逆序遍历,二分插入
从数组后端进行遍历,每次将一个元素x放入另一个列表中,利用二分查找搜索其应该插入的位置(相同则插入到相同元素的最左端),这个位置对应的索引等于原数组中右侧小于x的元素数量。
尝试结果:超出时间限制。挂在了倒数第二个测试样例,特征为大量重复元素。由于在处理相同元素时,采用的是逐个向前遍历的方式,在存在大量重复元素时处理效率较低。接下来需要对寻找插入相同元素的位置进行优化。
成功代码(二分插入)
经过上述尝试,现在所需要进行的优化就是寻找相同元素的插入位置。由于需要将x插入到相同元素的最前端,因此在使用二分法时,将大于和等于归为同一类即可。
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
def find(num):
left, right = 0,len(sortlist)
while left < right:
mid = (left + right) // 2
if sortlist[mid] < num:
left = mid + 1
elif sortlist[mid] >= num:
right = mid
sortlist.insert(left,num)
return left
length = len(nums)
counts = [0]*length
sortlist = [nums[-1]]
for i in range(length-2,-1,-1):
counts[i] = find(nums[i])
return counts
该方法成功通过测试。