题目
题目来源:力扣(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 。
思路分析
- 线段树区间查询⽤循环实现;
 - 对于每⼀个要插⼊的数x,只查询⼤于x的数。⼩于x的数⽤总数减出来。
 
/*** @param {number[]} instructions* @return {number}*/var Tree = function (maxNum) {var treeSize = maxNum & (maxNum - 1) == 0 ? 2 * maxNum : 4 * maxNum;this.treeArr = new Array(treeSize).fill(0);};Tree.prototype.getRangeCount = function (targetStart, targetEnd, currStart, currEnd, n = 0) {if (targetEnd < currStart || targetStart > currEnd) {return 0;}if (targetStart <= currStart && targetEnd >= currEnd) {return this.treeArr[n];}// 开始二分var temp = (currStart + currEnd) >> 1;var a = this.getRangeCount(targetStart, targetEnd, currStart, temp, n * 2 + 1);var b = this.getRangeCount(targetStart, targetEnd, temp + 1, currEnd, n * 2 + 2);return a + b;}Tree.prototype.addNum = function (num, currStart, currEnd, n = 0) {if (currStart <= num && currEnd >= num) {this.treeArr[n]++;if (currStart != currEnd) {// 二分赋值var temp = (currStart + currEnd) >> 1;this.addNum(num, currStart, temp, n * 2 + 1);this.addNum(num, temp + 1, currEnd, n * 2 + 2);}}}var createSortedArray = function (instructions) {// 直接使用搜索树无法获取相对数量!// 使用线段树统计!size = instructions.length;if (size < 3) {return 0;}var maxNum = Number.MIN_VALUE;for (i = 0; i < size; i++) {maxNum = Math.max(instructions[i], maxNum);}var tree = new Tree(maxNum);tree.addNum(instructions[0], 0, maxNum)tree.addNum(instructions[1], 0, maxNum)var ret = 0;var pivot = 1000000007;for (i = 2; i < size; i++) {// 比新插入值小的数字的数量var temp1 = tree.getRangeCount(0, instructions[i] - 1, 0, maxNum);temp1 = Math.min(temp1, i - temp1);// 比新插入值大的数字的数量var temp2 = tree.getRangeCount(instructions[i] + 1, maxNum, 0, maxNum);temp2 = Math.min(temp2, i - temp2);var cost = Math.min(temp1, temp2);ret += cost;ret = ret % pivot;tree.addNum(instructions[i], 0, maxNum);}return ret;};
