剑指 Offer 51. 数组中的逆序对

粗糙的代码,返回值都还不匹配:

  1. var reversePairs = function(nums) {
  2. let n = nums.length;
  3. if (n <= 1) {
  4. return 0;
  5. }
  6. let mid = Math.floor(n / 2);
  7. let lowArray = reversePairs(nums.slice(0, mid));
  8. let highArray = reversePairs(nums.slice(mid));
  9. let tmp = [];
  10. let i = 0;
  11. let j = 0;
  12. let count = 0;
  13. for (let x = 0; x < n; x++) {
  14. if (i === mid) {
  15. j++;
  16. continue;
  17. }
  18. if (j === (n - mid)) {
  19. i++;
  20. continue;
  21. }
  22. if (lowArray[i] < highArray[j]) {
  23. count++;
  24. i++;
  25. } else {
  26. j++;
  27. }
  28. }
  29. return count;
  30. };

reversePairs 希望返回的是个数,归并排序中的两个有序数组,可以考虑合并到一个数组(一般叫这个数组为帮助数组,这里起名 tmp)里返回,而且 tmp 可以做到全局唯一。之前总是隐隐戳戳地觉得可能下标关系对不上,其实不用操心,对于分治思想,操作的总是自己的区间,不会影响他人的。

写了一个上午,终于写完,磕磕巴巴的,得总结总结:

  1. var reversePairs = function(nums) {
  2. let n = nums.length;
  3. if (n <= 1) {
  4. return 0;
  5. }
  6. let count = 0;
  7. let tmp = new Array(n);
  8. function sort(left, right) { // 不穿数组,传下标
  9. if (left === right) {
  10. tmp[left] = nums[left];
  11. return;
  12. }
  13. let mid = left + Math.floor((right - left) / 2);
  14. sort(left, mid);
  15. sort(mid + 1, right);
  16. merge(left, mid, right);
  17. }
  18. function merge(left, mid, right) {
  19. let i = left; // 左
  20. let j = mid + 1;
  21. let arr = new Array(); // 这个结构能省吗?
  22. for (let x = left; x <= right; x++) {
  23. if (i === mid + 1) {
  24. arr[x] = tmp[j];
  25. j++;
  26. continue;
  27. }
  28. if (j === right + 1) {
  29. arr[x] = tmp[i];
  30. i++;
  31. continue;
  32. }
  33. if (tmp[i] > tmp[j]) {
  34. arr[x] = tmp[j];
  35. count += mid - i + 1;
  36. j++;
  37. } else {
  38. arr[x] = tmp[i];
  39. i++;
  40. }
  41. }
  42. for (let y = left; y <= right; y++) {
  43. tmp[y] = arr[y];
  44. }
  45. }
  46. sort(0, n - 1);
  47. return count;
  48. };

找到了我这种写法的弊病了,正确写法:(思想在注释里)

  1. function merge(left, mid, right) {
  2. // 1. 从 nums[left...right] 复制 到 tmp[left, right]
  3. for (let x = left; x <= right; x++) {
  4. tmp[x] = nums[x];
  5. }
  6. // 2. tmp[left, mid] 和 tmp[mid + 1, right] 归并排序覆盖到 nums[left...right] 里
  7. // 我之前不放心的一点是,tmp 里面的顺序怎么办,其实 在递归的第一步都是从 nums 里拿数据,tmp 就真是 暂时的
  8. let i = left; // 左
  9. let j = mid + 1;
  10. // let arr = new Array(); // 这个结构能省吗?
  11. for (let x = left; x <= right; x++) {
  12. if (i === mid + 1) {
  13. nums[x] = tmp[j];
  14. j++;
  15. continue;
  16. }
  17. if (j === right + 1) {
  18. nums[x] = tmp[i];
  19. i++;
  20. continue;
  21. }
  22. if (tmp[i] > tmp[j]) { // 出现逆序对
  23. nums[x] = tmp[j];
  24. count += mid - i + 1;
  25. j++;
  26. } else {
  27. nums[x] = tmp[i];
  28. i++;
  29. }
  30. }
  31. }

很神奇的是,提升非常的大!
image.png

image.png

315. 计算右侧小于当前元素的个数

  1. var countSmaller = function(nums) {
  2. let n = nums.length;
  3. if (n <= 1) {
  4. return [0];
  5. }
  6. let result = new Array(n).fill(0);
  7. let indexArray = new Array(n).fill().map((value, index) => index); // 引入索引数组
  8. let tmp = new Array(n);
  9. function sort(left, right) { // 不穿数组,传下标
  10. if (left === right) {
  11. return;
  12. }
  13. let mid = left + Math.floor((right - left) / 2);
  14. sort(left, mid);
  15. sort(mid + 1, right);
  16. merge(left, mid, right);
  17. }
  18. function merge(left, mid, right) {
  19. // 1. 从 nums[left...right] 复制 到 tmp[left, right]
  20. for (let x = left; x <= right; x++) {
  21. // tmp[x] = nums[x];
  22. tmp[x] = indexArray[x]; // 改为索引数组
  23. }
  24. let i = left; // 左
  25. let j = mid + 1;
  26. for (let x = left; x <= right; x++) {
  27. if (i === mid + 1) {
  28. indexArray[x] = tmp[j];
  29. j++;
  30. continue;
  31. }
  32. if (j === right + 1) {
  33. indexArray[x] = tmp[i];
  34. result[tmp[i]] += right - mid; // 这里要新增
  35. i++;
  36. continue;
  37. }
  38. if (nums[tmp[i]] <= nums[tmp[j]]) { // 需要归并的对象是 tmp 里的元素,明白这一点!!nums[]只是比较规则而已
  39. // indexArray[i] = j;
  40. // indexArray[tmp[i]] = tmp[j];
  41. indexArray[x] = tmp[i]; // 是这个
  42. result[tmp[i]] += j - mid - 1;
  43. i++;
  44. } else {
  45. indexArray[x] = tmp[j];
  46. j++;
  47. }
  48. }
  49. }
  50. sort(0, n - 1);
  51. return result;
  52. };

对比 51 和 351 的写法,非常的像:
image.png

493. 翻转对

  1. /**
  2. * @param {number[]} nums
  3. * @return {number}
  4. */
  5. var reversePairs = function(nums) {
  6. let n = nums.length;
  7. if (n <= 1) {
  8. return 0;
  9. }
  10. let count = 0;
  11. let tmp = new Array(n);
  12. sort(0, n - 1); // 左右都闭,容易处理
  13. function sort(left, right) {
  14. if (left === right) {
  15. return;
  16. }
  17. let mid = left + Math.floor((right - left) / 2);
  18. sort(left, mid);
  19. sort(mid + 1, right);
  20. merge(left, mid, right); // [left...mid] 和 [mid+1...right] 都有序
  21. function merge(left, mid, right) {
  22. for (let x = left; x <= right; x++) {
  23. tmp[x] = nums[x];
  24. }
  25. let i = left;
  26. let j = mid + 1;
  27. for (let k = left; k <= right; k++) {
  28. if (i === mid + 1) {
  29. nums[k] = tmp[j];
  30. j++;
  31. continue;
  32. }
  33. if (j === right + 1) {
  34. nums[k] = tmp[i];
  35. i++;
  36. continue;
  37. }
  38. if (tmp[i] <= tmp[j]) {
  39. nums[k] = tmp[i];
  40. i++;
  41. } else {
  42. nums[k] = tmp[j];
  43. j++;
  44. }
  45. }
  46. // 上面是正常的归并排序
  47. // 下面,tmp[i] > 2 * tmp[j] 这个比较会影响正常的排序,所以单独拿出出来
  48. i = left;
  49. j = mid + 1;
  50. for (let k = left; k <= right; k++) {
  51. if (i === mid + 1) {
  52. j++;
  53. continue;
  54. }
  55. if (j === right + 1) {
  56. i++;
  57. continue;
  58. }
  59. if (tmp[i] > 2 * tmp[j]) {
  60. count += mid - i + 1;
  61. j++;
  62. } else {
  63. i++;
  64. }
  65. }
  66. }
  67. }
  68. return count;
  69. };

327. 区间和的个数

朴素的暴力解,一点都不好看。

  1. var countRangeSum = function(nums, lower, upper) {
  2. let n = nums.length;
  3. let count = 0;
  4. let sum;
  5. for (let i = 0; i < n; i++) {
  6. sum = 0;
  7. for (let j = i; j <= n; j++) {
  8. sum += nums[j];
  9. if (lower <= sum && sum <= upper) {
  10. count++;
  11. }
  12. }
  13. }
  14. return count;
  15. }

这道题,看不出半点 需要归并排序的地方啊:
看不懂:
https://leetcode-cn.com/problems/count-of-range-sum/solution/qu-jian-he-de-ge-shu-by-leetcode-solution/
前缀和:
image.png