1. 问题背景与定义
给定一个整数数组nums,要求返回一个新数组counts,其中counts[i]表示在nums[i]右侧且小于nums[i]的元素的数量。例如:
输入: [5,2,6,1]
输出: [2,1,1,0]
这个问题的直观解法是双重循环暴力枚举,时间复杂度为O(n²)。但在实际工程中,当数据规模达到10⁵级别时,这种解法显然无法满足性能需求。
2. 分治算法设计思路
2.1 归并排序的启发
归并排序的核心思想是将数组分成两半分别排序后再合并。在这个过程中,我们可以利用"合并"阶段的有序特性来统计逆序对:
- 当左半部分的元素被选中放入合并数组时,说明它比右半部分当前指针之前的所有元素都大
- 这些右半部分的元素就是原数组中位于该元素右侧且比它小的元素
2.2 具体实现方案
我们需要在归并排序的过程中维护两个关键信息:
- 原始索引到当前索引的映射(因为排序会打乱元素位置)
- 每个原始位置对应的计数结果
采用索引数组来记录原始位置,在元素交换时同步更新索引数组。
3. 算法实现细节
3.1 数据结构设计
python复制def countSmaller(nums):
n = len(nums)
result = [0] * n
indices = list(range(n)) # 记录原始索引
temp = [0] * n # 归并临时数组
temp_idx = [0] * n # 临时索引数组
def merge_sort(left, right):
if left >= right:
return
mid = (left + right) // 2
merge_sort(left, mid)
merge_sort(mid + 1, right)
merge(left, mid, right)
def merge(left, mid, right):
i, j = left, mid + 1
k = left
# 统计逆序数
while i <= mid and j <= right:
if nums[indices[i]] <= nums[indices[j]]:
temp[k] = indices[i]
result[indices[i]] += j - (mid + 1)
i += 1
else:
temp[k] = indices[j]
j += 1
k += 1
# 处理剩余元素
while i <= mid:
temp[k] = indices[i]
result[indices[i]] += j - (mid + 1)
i += 1
k += 1
while j <= right:
temp[k] = indices[j]
j += 1
k += 1
# 拷贝回原数组
for p in range(left, right + 1):
indices[p] = temp[p]
merge_sort(0, n - 1)
return result
3.2 关键步骤解析
- 索引数组维护:
indices数组始终保持原始位置到当前排序位置的映射关系 - 逆序统计时机:当左半部分元素被选中时,右半部分当前指针之前的所有元素都比它小
- 结果累加:
result[indices[i]] += j - (mid + 1)这行代码实现了核心计数逻辑
4. 复杂度分析与优化
4.1 时间复杂度
- 归并排序本身的时间复杂度为O(nlogn)
- 每次合并操作中的计数操作是O(1)的
- 总体时间复杂度保持为O(nlogn)
4.2 空间复杂度
- 需要额外的O(n)空间存储索引数组和临时数组
- 属于典型的空间换时间策略
4.3 工程优化方向
- 对小规模子数组改用插入排序(通常当n<15时)
- 预先检查数组是否已有序,避免不必要操作
- 使用循环展开等底层优化技巧
5. 边界条件与测试用例
5.1 典型测试场景
python复制测试用例1: [5,2,6,1] → [2,1,1,0]
测试用例2: [1,1,1,1] → [0,0,0,0]
测试用例3: [7,6,5,4] → [3,2,1,0]
测试用例4: [] → []
测试用例5: [1] → [0]
5.2 特殊边界处理
- 空数组输入直接返回空数组
- 单元素数组返回[0]
- 处理整数溢出情况(虽然Python不需要考虑)
- 处理非常大的输入规模(10⁵级别)
6. 实际应用场景
6.1 推荐系统
在协同过滤算法中,需要统计用户行为序列中的相对偏好关系,这种右侧小于当前元素的统计可以用于衡量项目流行度的局部相对性。
6.2 金融分析
在分析股票价格序列时,统计每个价格点后面有多少个更低的价位,可以反映市场情绪的转变点。
6.3 竞赛排名系统
处理实时更新的选手得分时,快速计算每个选手当前排名变化情况。
7. 算法变种与扩展
7.1 统计大于当前元素的个数
只需修改比较条件为nums[indices[i]] >= nums[indices[j]],并调整计数逻辑。
7.2 二维平面上的逆序点对
将问题扩展到二维空间,先按x坐标排序,然后在y方向上进行类似统计。
7.3 带权重的逆序统计
每个元素带有权重值,统计右侧小于当前元素的权重和,需要在归并时额外维护前缀和数组。