根据前面两篇文章
区间合并
差分数组
对差分数组和合并区间的介绍,以下是两道相关的例题,其中综合题融合了区间合并和差分数组,非常经典,也有点难度,值得仔细琢磨
最合适的价格 (差分数组)
给定一个二维数组prices,price[i] 表示第 i 个商户能接受的价格区间范围。
商品总售价 = 店家数 * 价格,要想使得商品总售价最高,求最小的价格是?
例子:
[
[1, 9],
[7, 12],
[8, 15],
]
价格为9 时,总售价最高
思路:构造一个map price ⇒ 商户个数,因为只有端点和(端点前)可能出现最大值,因此只需要用diffMap 记录即可,不需要使用diffArr 记录区间里的每个点。
const getBestPrice = (prices) => {
// 实现一个diffMap
const diffMap = new Map();
for (let i = 0; i < prices.length; i++) {
const [start, end] = prices[i];
if (!diffMap.has(start)) {
diffMap.set(start, 0);
}
if (!diffMap.has(end + 1)) {
diffMap.set(end + 1, 0);
}
diffMap.set(end + 1, diffMap.get(end + 1) - 1);
diffMap.set(start, diffMap.get(start) + 1);
}
const sortedDiffMap = new Map(
[...diffMap.entries()].sort((a, b) => a[0] - b[0])
);
let sum = 0;
let res = 0;
let max = 0;
let flag = false;
for (const [price, count] of sortedDiffMap.entries()) {
if (!flag) {
//第一次
sum += count;
if(sum * price > max){
max = sum * price;
res = price;
}
flag = true;
} else {
// 如果不是第一次出现的结点,需要记录它前面一个点的价格
if(sum * (price - 1) > max){
max = sum * (price - 1);
res = price - 1;
}
sum += count;
}
}
return res;
};
console.log(getBestPrice([[3,5], [4,6], [9,10]]));
console.log(
getBestPrice([
[7, 9],
[8, 10],
[9, 10],
])
);
console.log(
getBestPrice([
[1, 9],
[7, 12],
[8, 15],
])
);
空闲服务器数 (合并与区间的综合题)
有serverNum 个服务器,给出taskList,taskList[i] = {startTime,endTime,serveId} 表示某个服务器serveceId 在startTime 到 endTime 被占用。
求在哪些时间仅有一台服务器是空闲的。
思路:
- 用map 将相同任务id被占用的时间进行聚合
- 对同一个id的任务进行区间合并
- 构造差分Map,是开区间
- 利用差分Map还原出数组时维护最终答案,不必通过还原后再去遍历寻找结果。
const mergeIntervals = (intervals) => {
let merged = [intervals[0]];
for (let i = 1; i < intervals.length; i++) {
const [start, end] = intervals[i];
if (merged[merged.length - 1][1] < start) {
merged.push([start, end]);
} else {
merged[merged.length - 1][1] = Math.max(
merged[merged.length - 1][1],
end
);
}
}
return merged;
};
const buildDiffMap = (allTaskList) => {
let diffMap = new Map();
for (let i = 0; i < allTaskList.length; i++) {
const [start, end] = allTaskList[i];
if (!diffMap.has(start)) {
diffMap.set(start, 0);
}
if (!diffMap.has(end)) {
diffMap.set(end, 0);
}
diffMap.set(start, diffMap.get(start) + 1);
diffMap.set(end, diffMap.get(end) - 1);
// diffMap.set(start, diffMap.get(start)+1 || 1 ); // 可能会出现 0 || 1 就会取后面的1
// diffMap.set(end, diffMap.get(end)-1 || -1);
}
return diffMap;
};
const getIntervals = (diffMap, target) => {
let ans = [];
let left = -1;
let sum = 0;
for (const [index, times] of diffMap.entries()) {
sum += times;
if (sum === target && left === -1) {
left = index;
}
if (sum !== target && left !== -1) {
ans.push([left, index]);
left = -1;
}
}
return ans;
};
/**
* 待实现函数,在此函数中填入答题代码
* @param {number} serverNum
* @param {Task[]} tasks
* @return {number[][]}
*/
const getOneFreeTime = (serverNum, tasks) => {
// 遍历tasks,将每个服务器的任务存起来。
const taskMap = new Map();
tasks.forEach((task) => {
const { startTime, endTime, serverId } = task;
taskMap.set(serverId, taskMap.get(serverId) || []);
taskMap.get(serverId).push([startTime, endTime]);
});
// 遍历taskMap ,对每个服务器下的任务进行合并并且存放到taskList里。
const allTaskList = [];
for (const [serverId, taskList] of taskMap.entries()) {
taskList.sort((a, b) => a[0] - b[0]);
let merged = mergeIntervals(taskList);
allTaskList.push(...merged);
}
// 使用diffMap 而不是 数组 提高性能
let diffMap = buildDiffMap(allTaskList);
// 对diffMap按照key进行排序。
let sortedDiffMap = new Map(
[...diffMap.entries()].sort((a, b) => a[0] - b[0])
);
// 根据diffMap算结果数组,并且边计算边维护答案
let ans = getIntervals(sortedDiffMap, serverNum - 1);
// // 遍历sortedTaskList,差分数组记录端点值
// let len = sortedTaskList[sortedTaskList.length-1][1];
// let diff = new Array(len).fill(0);
// for(let i = 0;i<sortedTaskList.length;i++){
// const [start,end] = sortedTaskList[i];
// diff[start-1]++;
// diff[end-1]--;
// }
// // 根据diff数组还原结果数组
// let result = [diff[0]];
// for(let i = 1;i<diff.length;i++){
// result.push(result[result.length-1]+diff[i]);
// }
// // 根据结果数组返回符合条件的区间。
// let ans = [];
// let left = -1;
// for(let i = 0;i<result.length;i++){
// if(result[i]===serverNum-1 && left===-1){
// left = i;
// }else if(result[i]!==serverNum-1 && left!==-1){
// ans.push([left+1,i+1]);
// left = -1;
// }
// }
return ans.length === 0 ? [[-1, -1]] : ans;
};