每天学习一点算法 2026/05/26
题目:计算右侧小于当前元素的个数
给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: countsi 的值是 numsi 右侧小于 numsi 的元素的数量。
这题思路很简单,就是遍历数组 nums ,计算后续元素小于自身的个数。重点在于如何优化时间复杂度。
假如我们从右侧开始遍历,将已经遍历的元素进行降序排序,那么我们只需要在元素 nums[i] 右侧已排序的元素中找到第一个小于 nums[i] 的元素 nums[j] , nums[j] 及它后面的元素就都是小于 nums[i] 的,而且我们找到的位置 j 正是我们进行后续排序插入的 nums[i] 的位置,所以我们可以计算 count[i] 的同时进行排序操作。
由于实在排序数组中寻找元素,所以我们可以利用二分查找进一步进行优化。
typescript
function countSmaller(nums: number[]): number[] {
const len = nums.length
if (len === 1) return [0] // 长度为 1 直接返回 [0]
const counts = new Array(len).fill(0) // 结果数组
const sortList: number[] = [nums[len - 1]] // 已排序部分
// 从右往左遍历
for (let i = len - 2; i >= 0; i--) {
const j = findSmaller(nums[i]) // 获取当前元素插入位置
if (j === sortList.length) {
// 如果插入位置在末尾,表示没有小于它的元素
sortList.push(nums[i])
counts[i] = 0
} else {
// 插入位置后的元素个数就是小于它的元素个数
counts[i] = sortList.length - j
sortList.splice(j, 0, nums[i])
}
}
return counts
// 辅助函数二分查找第一个小于目标的元素的位置
function findSmaller (target: number): number {
let left = 0, right = sortList.length - 1
let res = sortList.length
while (left <= right) {
const mid = Math.floor((left + right) / 2)
if (target > sortList[mid]) {
res = mid
right = mid - 1
} else {
left = mid + 1
}
}
return res
}
};
我还纳闷这题怎么在树和图的题解里面,看了官方题解提到了树状数组,emmm 有点复杂。待我研究研究,明天写一篇 树状数组 的文章
题目来源:力扣(LeetCode)