【差分数组&& 区间的综合用例】

根据前面两篇文章
区间合并
差分数组
对差分数组和合并区间的介绍,以下是两道相关的例题,其中综合题融合了区间合并和差分数组,非常经典,也有点难度,值得仔细琢磨

最合适的价格 (差分数组)

给定一个二维数组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
  //     const [start,end] = sortedTaskList[i];
  //     diff[start-1]++;
  //     diff[end-1]--;
  // }

  // // 根据diff数组还原结果数组
  // let result = [diff[0]];
  // for(let i = 1;i
  //     result.push(result[result.length-1]+diff[i]);
  // }

  // // 根据结果数组返回符合条件的区间。
  // let ans = [];
  // let left = -1;
  // for(let i = 0;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;
};

你可能感兴趣的:(#,LeetCode刻意练习,javascript,前端,开发语言)