剑指 Offer 51. 数组中的逆序对
粗糙的代码,返回值都还不匹配:
var reversePairs = function(nums) {let n = nums.length;if (n <= 1) {return 0;}let mid = Math.floor(n / 2);let lowArray = reversePairs(nums.slice(0, mid));let highArray = reversePairs(nums.slice(mid));let tmp = [];let i = 0;let j = 0;let count = 0;for (let x = 0; x < n; x++) {if (i === mid) {j++;continue;}if (j === (n - mid)) {i++;continue;}if (lowArray[i] < highArray[j]) {count++;i++;} else {j++;}}return count;};
reversePairs 希望返回的是个数,归并排序中的两个有序数组,可以考虑合并到一个数组(一般叫这个数组为帮助数组,这里起名 tmp)里返回,而且 tmp 可以做到全局唯一。之前总是隐隐戳戳地觉得可能下标关系对不上,其实不用操心,对于分治思想,操作的总是自己的区间,不会影响他人的。
写了一个上午,终于写完,磕磕巴巴的,得总结总结:
var reversePairs = function(nums) {let n = nums.length;if (n <= 1) {return 0;}let count = 0;let tmp = new Array(n);function sort(left, right) { // 不穿数组,传下标if (left === right) {tmp[left] = nums[left];return;}let mid = left + Math.floor((right - left) / 2);sort(left, mid);sort(mid + 1, right);merge(left, mid, right);}function merge(left, mid, right) {let i = left; // 左let j = mid + 1;let arr = new Array(); // 这个结构能省吗?for (let x = left; x <= right; x++) {if (i === mid + 1) {arr[x] = tmp[j];j++;continue;}if (j === right + 1) {arr[x] = tmp[i];i++;continue;}if (tmp[i] > tmp[j]) {arr[x] = tmp[j];count += mid - i + 1;j++;} else {arr[x] = tmp[i];i++;}}for (let y = left; y <= right; y++) {tmp[y] = arr[y];}}sort(0, n - 1);return count;};
找到了我这种写法的弊病了,正确写法:(思想在注释里)
function merge(left, mid, right) {// 1. 从 nums[left...right] 复制 到 tmp[left, right]for (let x = left; x <= right; x++) {tmp[x] = nums[x];}// 2. tmp[left, mid] 和 tmp[mid + 1, right] 归并排序覆盖到 nums[left...right] 里// 我之前不放心的一点是,tmp 里面的顺序怎么办,其实 在递归的第一步都是从 nums 里拿数据,tmp 就真是 暂时的let i = left; // 左let j = mid + 1;// let arr = new Array(); // 这个结构能省吗?for (let x = left; x <= right; x++) {if (i === mid + 1) {nums[x] = tmp[j];j++;continue;}if (j === right + 1) {nums[x] = tmp[i];i++;continue;}if (tmp[i] > tmp[j]) { // 出现逆序对nums[x] = tmp[j];count += mid - i + 1;j++;} else {nums[x] = tmp[i];i++;}}}
很神奇的是,提升非常的大!

315. 计算右侧小于当前元素的个数
var countSmaller = function(nums) {let n = nums.length;if (n <= 1) {return [0];}let result = new Array(n).fill(0);let indexArray = new Array(n).fill().map((value, index) => index); // 引入索引数组let tmp = new Array(n);function sort(left, right) { // 不穿数组,传下标if (left === right) {return;}let mid = left + Math.floor((right - left) / 2);sort(left, mid);sort(mid + 1, right);merge(left, mid, right);}function merge(left, mid, right) {// 1. 从 nums[left...right] 复制 到 tmp[left, right]for (let x = left; x <= right; x++) {// tmp[x] = nums[x];tmp[x] = indexArray[x]; // 改为索引数组}let i = left; // 左let j = mid + 1;for (let x = left; x <= right; x++) {if (i === mid + 1) {indexArray[x] = tmp[j];j++;continue;}if (j === right + 1) {indexArray[x] = tmp[i];result[tmp[i]] += right - mid; // 这里要新增i++;continue;}if (nums[tmp[i]] <= nums[tmp[j]]) { // 需要归并的对象是 tmp 里的元素,明白这一点!!nums[]只是比较规则而已// indexArray[i] = j;// indexArray[tmp[i]] = tmp[j];indexArray[x] = tmp[i]; // 是这个result[tmp[i]] += j - mid - 1;i++;} else {indexArray[x] = tmp[j];j++;}}}sort(0, n - 1);return result;};
493. 翻转对
/*** @param {number[]} nums* @return {number}*/var reversePairs = function(nums) {let n = nums.length;if (n <= 1) {return 0;}let count = 0;let tmp = new Array(n);sort(0, n - 1); // 左右都闭,容易处理function sort(left, right) {if (left === right) {return;}let mid = left + Math.floor((right - left) / 2);sort(left, mid);sort(mid + 1, right);merge(left, mid, right); // [left...mid] 和 [mid+1...right] 都有序function merge(left, mid, right) {for (let x = left; x <= right; x++) {tmp[x] = nums[x];}let i = left;let j = mid + 1;for (let k = left; k <= right; k++) {if (i === mid + 1) {nums[k] = tmp[j];j++;continue;}if (j === right + 1) {nums[k] = tmp[i];i++;continue;}if (tmp[i] <= tmp[j]) {nums[k] = tmp[i];i++;} else {nums[k] = tmp[j];j++;}}// 上面是正常的归并排序// 下面,tmp[i] > 2 * tmp[j] 这个比较会影响正常的排序,所以单独拿出出来i = left;j = mid + 1;for (let k = left; k <= right; k++) {if (i === mid + 1) {j++;continue;}if (j === right + 1) {i++;continue;}if (tmp[i] > 2 * tmp[j]) {count += mid - i + 1;j++;} else {i++;}}}}return count;};
327. 区间和的个数
朴素的暴力解,一点都不好看。
var countRangeSum = function(nums, lower, upper) {let n = nums.length;let count = 0;let sum;for (let i = 0; i < n; i++) {sum = 0;for (let j = i; j <= n; j++) {sum += nums[j];if (lower <= sum && sum <= upper) {count++;}}}return count;}
这道题,看不出半点 需要归并排序的地方啊:
看不懂:
https://leetcode-cn.com/problems/count-of-range-sum/solution/qu-jian-he-de-ge-shu-by-leetcode-solution/
前缀和:
