给你一个整数数组
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 <= instructions.length <= 10^5
1 <= instructions[i] <= 10^5
/**
* @param {number[]} instructions
* @return {number}
*/
// 线段树去查询用循环实现
// 对每一个要插入的值 x,只需要查询大于x的数,小于x的数 用总数剪出来
var Tree = function(maxNum){
var treeSize = maxNum & (maxNum - 1) === 0 ? 2 * maxNum : 4 * maxNum;
this.treeArray = 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.treeArray[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.treeArray[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(let 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 = 1e9 + 7;
for(let 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;
};