题目看起来很简单,但一试就知道 O(N^2) 的暴力解法是会超时的。所以我想到了肯定是需要降低复杂度到 O(N log N) ,但苦于猪脑过载实在想不出来什么方法。想到可能会使用二叉搜索树,但是感觉是需要维持平衡的,遂放弃。
看了这篇答案之后理解了一下才明白其中的奥秘。这个方法实际上是通过在归并排序的过程中,找到右侧数组中比左侧数组中元素小的个数来计算,因为理想状态下右侧的所有元素都应该比左侧大才对。
现在看来想要降低复杂度也是有几个思路,但大多都是二分、归并这种方法。这道题目之所以是困难,主要也是在于即便想到二分,可能也没有头绪发现这个规律吧。
代码如下:
const countSmaller = nums => {
const newArr = nums.map((n, i) => ([n, i]));
const result = Array(nums.length).fill(0);
mergeSortAndCount(newArr, 0, nums.length - 1, result);
return result;
};
const mergeSortAndCount = (nums, start, end, result) => {
if (start >= end) return;
const mid = Math.floor((start + end) / 2);
mergeSortAndCount(nums, start, mid, result);
mergeSortAndCount(nums, mid + 1, end, result);
let left = start;
let right = mid + 1;
let numRightLessLeft = 0;
const merged = [];
while(left < mid + 1 && right <= end) {
if (nums[left][0] > nums[right][0]) {
numRightLessLeft++;
merged.push(nums[right]);
right++;
} else {
merged.push(nums[left]);
result[nums[left][1]] += numRightLessLeft;
left++;
}
}
while (left < mid + 1) {
merged.push(nums[left]);
result[nums[left][1]] += numRightLessLeft;
left++;
}
while (right <= end) {
merged.push(nums[right]);
right++;
}
merged.forEach((tuple, index) => {
nums[start + index] = tuple;
})
}