题目
给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
题解
循环+二分
比如数组nums = [5,2,6,1];临时数组list;结果数据result = [];
从右到左枚举第1个位置1,list此时为空,将1放入list = [1]; result.unshift(0)
第2个位置6,将6通过二分法插入list数组,得到list = [6,1],6左边有1位数,result.unshift(1);
第3个位置2,将2通过二分法插入list数组,得到list = [6,2,1],2左边有1位数,result.unshift(1);
第4个位置5,将5通过二分法插入list数组,得到list = [6,5,2,1],5左边有2位数,result.unshift(2);
结果返回resutl = [2,1,1,0]
代码
var countSmaller = function (nums) {
const len = nums.length
let list = []
let result = Array(len)
for (let i = len - 1; i >= 0; i--) {
let l = 0
let r = list.length
while (l < r) {
const mid = l + Math.floor((r - l) / 2)
if (list[mid] >= nums[i]) {
r = mid
} else {
l = mid + 1
}
}
result[i] = l
list.splice(l, 0, nums[i])
}
return result
}
归并
- 利用归并排序思想
- 归并过程将数组分为有序的left和right
- 在合并left和right过程中,对于任意left中元素,right的索引x即为原数组中当前元素左侧有n+x个小于当前元素的值;其中n是上一次【并】过来的数量
- 归并过程中如何知道当前元素中在原数组的下标?
- 利用数组对象,将nums数组重新组合变成{num:'值',index,'下标'}
根据上述思路编辑代码如下
var countSmaller = function (nums) {
const len = nums.length
let result = Array(len).fill(0)
const list = nums.map((v, i) => ({ num: v, index: i }))
mergeSort(list)
return result
function mergeSort(nums) {
//获取数组长度
const len = nums.length
//如果数组长度小于2返回数组
if (len < 2) return nums
// 否则分割数组
const mid = len >> 1 //右移1位,类似除以2
// 递归分解左侧数组
const left = mergeSort(nums.slice(0, mid))
// 递归分解右侧数组
const right = mergeSort(nums.slice(mid, len))
// 合并左右两个数组
return merge(left, right)
// 合并数组
}
function merge(left, right) {
let l = 0
let r = 0
let list = []
while (l < left.length && r < right.length) {
//console.log('left', left)
if (left[l].num <= right[r].num) {
list.push(left[l])
result[left[l].index] += r
l++
} else {
list.push(right[r])
r++
}
}
if (l === left.length) {
list = list.concat(right.slice(r, right.length))
}
if (r === right.length) {
while (l < left.length) {
list.push(left[l])
result[left[l].index] += r
l++
}
}
return list
}
}