题目链接
315. 计算右侧小于当前元素的个数 - 力扣(LeetCode)
题解
前置概念:
- in 和 of 区别
in 操作符:遍历或者检查对象的键名,遍历的是可枚举属性名,不适合遍历数组,会遍历原型链上的属性
of 操作符:遍历可迭代对象的键值(数组,字符串,Map,Set 等,不能遍历普通对象,除非定义了 Symbol.iterotor)不会遍历原型链
- size 和 length 区别
length 适合用于类数组 (数组,字符串),表示索引的个数
size 用于集合类型,表示键值对/元素的个数
- lowBit 函数
作用是 只保留一个整数的二进制中最右边的1,并将其余位置全部为0
利用了计算机中负数以补码的形式存储的特性
为什么 x & (-x) 能留下最右边的 1?
-
对于 x 的最右边的 1,设它在第 k 位
-
这个 1 右边的所有位都是 0
-
当对 x 取反时,第 k 位变成 0,右边的 0 都变成 1
-
再加 1 时,进位会把第 k 位重新变成 1,而右边又变回 0
-
这样,-x 的第 k 位也是 1,且第 k 位右边的位都是 0
-
所以 x & (-x) 的结果中,只有第 k 位是 1
- 树状数组:
思路:
先排序然后构建一个树状数组,从后往前开始计算遍历构建一个树状数组,根据当前的 nums[i] 缓存下标到 map 里面(没用二分查找是因为时间超限,使用这个是以空间换时间,事件复杂度从logn 到 1),最后输出倒序的结果即可
代码
javascript
var countSmaller = function(nums) {
let c = [];
let a = [];
let valueToIndex = new Map();
function init(length) {
return new Array(length + 5).fill(0);
}
function lowBit(x) {
return x & (-x);
}
function update(pos) {
while (pos < c.length) {
c[pos]++;
pos += lowBit(pos);
}
}
function query(pos) {
let resNum = 0;
while (pos > 0) {
resNum += c[pos];
pos -= lowBit(pos);
}
return resNum;
}
function discretization(nums) {
// 去重并排序
let set = new Set(nums);
a = Array.from(set).sort((x, y) => x - y);
// 建立映射(索引从1开始)
a.forEach((val, idx) => {
valueToIndex.set(val, idx + 1);
});
}
function getRes() {
let res = [];
discretization(nums);
c = init(nums.length);
for (let i = nums.length - 1; i >= 0; i--) {
let id = valueToIndex.get(nums[i]); // O(1) 获取
res.push(query(id - 1)); // 查询比它小的个数
update(id); // 把自己加进去
}
return res.reverse();
}
return getRes();
};