题目

题目来源:力扣(LeetCode)

给你一个整数数组 instructions ,你需要根据 instructions 中的元素创建一个有序数组。一开始你有一个空的数组 nums ,你需要 从左到右 遍历 instructions 中的元素,将它们依次插入 nums 数组中。每一次插入操作的 代价 是以下两者的 较小值 :

nums 中 严格小于 instructions[i] 的数字数目。
nums 中 严格大于 instructions[i] 的数字数目。
比方说,如果要将 3 插入到 nums = [1,2,3,5] ,那么插入操作的 代价 为 min(2, 1) (元素 1 和 2 小于 3 ,元素 5 大于 3 ),插入后 nums 变成 [1,2,3,3,5] 。

请你返回将 instructions 中所有元素依次插入 nums 后的 总最小代价 。由于答案会很大,请将它对 109 + 7 取余 后返回。

示例 1:

输入:instructions = [1,5,6,2]
输出:1
解释:一开始 nums = [] 。
插入 1 ,代价为 min(0, 0) = 0 ,现在 nums = [1] 。
插入 5 ,代价为 min(1, 0) = 0 ,现在 nums = [1,5] 。
插入 6 ,代价为 min(2, 0) = 0 ,现在 nums = [1,5,6] 。
插入 2 ,代价为 min(1, 2) = 1 ,现在 nums = [1,2,5,6] 。
总代价为 0 + 0 + 0 + 1 = 1 。

示例 2:

输入:instructions = [1,2,3,6,5,4]
输出:3
解释:一开始 nums = [] 。
插入 1 ,代价为 min(0, 0) = 0 ,现在 nums = [1] 。
插入 2 ,代价为 min(1, 0) = 0 ,现在 nums = [1,2] 。
插入 3 ,代价为 min(2, 0) = 0 ,现在 nums = [1,2,3] 。
插入 6 ,代价为 min(3, 0) = 0 ,现在 nums = [1,2,3,6] 。
插入 5 ,代价为 min(3, 1) = 1 ,现在 nums = [1,2,3,5,6] 。
插入 4 ,代价为 min(3, 2) = 2 ,现在 nums = [1,2,3,4,5,6] 。
总代价为 0 + 0 + 0 + 0 + 1 + 2 = 3 。

示例 3:

输入:instructions = [1,3,3,3,2,4,2,1,2]
输出:4
解释:一开始 nums = [] 。
插入 1 ,代价为 min(0, 0) = 0 ,现在 nums = [1] 。
插入 3 ,代价为 min(1, 0) = 0 ,现在 nums = [1,3] 。
插入 3 ,代价为 min(1, 0) = 0 ,现在 nums = [1,3,3] 。
插入 3 ,代价为 min(1, 0) = 0 ,现在 nums = [1,3,3,3] 。
插入 2 ,代价为 min(1, 3) = 1 ,现在 nums = [1,2,3,3,3] 。
插入 4 ,代价为 min(5, 0) = 0 ,现在 nums = [1,2,3,3,3,4] 。
插入 2 ,代价为 min(1, 4) = 1 ,现在 nums = [1,2,2,3,3,3,4] 。
插入 1 ,代价为 min(0, 6) = 0 ,现在 nums = [1,1,2,2,3,3,3,4] 。
插入 2 ,代价为 min(2, 4) = 2 ,现在 nums = [1,1,2,2,2,3,3,3,4] 。
总代价为 0 + 0 + 0 + 0 + 1 + 0 + 1 + 0 + 2 = 4 。

思路分析

  1. 线段树区间查询⽤循环实现;
  2. 对于每⼀个要插⼊的数x,只查询⼤于x的数。⼩于x的数⽤总数减出来。
  1. /**
  2. * @param {number[]} instructions
  3. * @return {number}
  4. */
  5. var Tree = function (maxNum) {
  6. var treeSize = maxNum & (maxNum - 1) == 0 ? 2 * maxNum : 4 * maxNum;
  7. this.treeArr = new Array(treeSize).fill(0);
  8. };
  9. Tree.prototype.getRangeCount = function (targetStart, targetEnd, currStart, currEnd, n = 0) {
  10. if (targetEnd < currStart || targetStart > currEnd) {
  11. return 0;
  12. }
  13. if (targetStart <= currStart && targetEnd >= currEnd) {
  14. return this.treeArr[n];
  15. }
  16. // 开始二分
  17. var temp = (currStart + currEnd) >> 1;
  18. var a = this.getRangeCount(targetStart, targetEnd, currStart, temp, n * 2 + 1);
  19. var b = this.getRangeCount(targetStart, targetEnd, temp + 1, currEnd, n * 2 + 2);
  20. return a + b;
  21. }
  22. Tree.prototype.addNum = function (num, currStart, currEnd, n = 0) {
  23. if (currStart <= num && currEnd >= num) {
  24. this.treeArr[n]++;
  25. if (currStart != currEnd) {
  26. // 二分赋值
  27. var temp = (currStart + currEnd) >> 1;
  28. this.addNum(num, currStart, temp, n * 2 + 1);
  29. this.addNum(num, temp + 1, currEnd, n * 2 + 2);
  30. }
  31. }
  32. }
  33. var createSortedArray = function (instructions) {
  34. // 直接使用搜索树无法获取相对数量!
  35. // 使用线段树统计!
  36. size = instructions.length;
  37. if (size < 3) {
  38. return 0;
  39. }
  40. var maxNum = Number.MIN_VALUE;
  41. for (i = 0; i < size; i++) {
  42. maxNum = Math.max(instructions[i], maxNum);
  43. }
  44. var tree = new Tree(maxNum);
  45. tree.addNum(instructions[0], 0, maxNum)
  46. tree.addNum(instructions[1], 0, maxNum)
  47. var ret = 0;
  48. var pivot = 1000000007;
  49. for (i = 2; i < size; i++) {
  50. // 比新插入值小的数字的数量
  51. var temp1 = tree.getRangeCount(0, instructions[i] - 1, 0, maxNum);
  52. temp1 = Math.min(temp1, i - temp1);
  53. // 比新插入值大的数字的数量
  54. var temp2 = tree.getRangeCount(instructions[i] + 1, maxNum, 0, maxNum);
  55. temp2 = Math.min(temp2, i - temp2);
  56. var cost = Math.min(temp1, temp2);
  57. ret += cost;
  58. ret = ret % pivot;
  59. tree.addNum(instructions[i], 0, maxNum);
  60. }
  61. return ret;
  62. };