题目重点:只统计严格小于,重复值不会互计。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 整数数组,输出与原位置对应的数量;标题相似线索采用标准严格小于训练合同。
- 若正式变体是≤或距离范围,要修改查询边界或过期状态。
解题路线
- 压缩排序不同值,逆序时树内恰存当前右侧所有元素。query(rank−1)给更小频次,随后插当前rank。
- 0基压缩rank对应Fenwick插入rank+1;查询i=rank恰覆盖0..rank−1,这是最常见的边界风险。
- 返回逆序累积结果再反转。排序压缩O(nlogn),不是原数组本身排序后返回,位置顺序必须保留。
手动走一遍
[5,2,6,1]输出[2,1,1,0]。[2,2,1]前两个2各只计右侧1,不计另一2,结果[1,1,0]。
Python 参考实现
下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。
代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。
from bisect import bisect_left
def count_smaller(a):
values=sorted(set(a));tree=[0]*(len(values)+1);answer=[]
for x in reversed(a):
rank=bisect_left(values,x);i=rank;count=0
while i:count+=tree[i];i-=i&-i
answer.append(count);i=rank+1
while i<len(tree):tree[i]+=1;i+=i&-i
return answer[::-1]
复杂度
O(nlogn)时间、O(n+d)空间;Fenwick单更新/查询O(logd),d不同值。
易错点与追问
容易失分的边界
- 当前值先入树再查询≤时可能计自己;本文查严格小可免自计但仍保持清晰先查后加。
- 统计全局更小值会包括左边元素。
继续深挖
- 只看后W个位置如何加右侧过期删除?
- merge-sort计逆序贡献怎样避免数据移动后丢原index?
