贪心算法之435区间问题

90 阅读3分钟

什么是贪心算法

贪心算法是一种常用的算法思想,它是一种在每一步选择中,都采取当前状态下最优选择,从而希望得到全局最优解的算法。

贪心算法通常分为两种类型:

  1. 基于贪心策略的优化问题:该类问题中,需要对某个目标函数,进行最大化或最小化优化,而目标函数通常又可以分解为若干个子问题,并且每个子问题的最优解,可以直接得到。因此我们可以采取贪心策略,即在每个子问题中都选择当前最优解,最终得到全局最优解。
  2. 基于贪心思想的组合优化问题:该类问题中,需要从一个集合中选择一部分元素,使得这些元素满足某些条件,并且达到某个目标。在这种问题中,我们也可以采取贪心思想,即在每一步中都选择当前最优的元素,最终得到一个可行解或最优解。

贪心算法的优点是简单、高效,容易实现和调试。但是,贪心算法不能保证得到全局最优解,因此在某些情况下可能会得到次优解或不可行解。

因此,在使用贪心算法时需要注意问题的特殊性和局限性,确保问题可以适用贪心算法。

  1. 题目描述

给定多个区间,计算让这些区间互不重叠,所需要移除区间的最少个数。起止相连不算重叠。
例子: 输⼊是⼀个数组,数组由多个长度固定为 2 的数组组成,表示区间的开始和结尾。输出一个整数,表示需要移除的区间数量量。

Input: [[1,2], [2,4], [1,3]]

Output: 1

在这个样例例中,我们可以移除区间 [1,3],使得剩余的区间 [[1,2], [2,4]] 互不不重叠

  1. 思考

这⾥的贪⼼策略使⽤是不明显的,需要做一些转换,但是如何转换呢?

这个⾸得⾃⼰思考下,然后才能有收获,不然看完题解也很快就忘记了了。

思考最好的⽅法是多写几个测试⽤例,看下如何解决。

⽐如测试用例中[[1,2], [2,4], [1,3]],为什什么会删除[1,3]呢?

很明显是[1,3]与[1,2]的区别就是3⽐2大,如果[1,3] 变成[0,5]呢?

则输⼊的数组变成[[1,2], [2,4], [0,5]],可以很明显看出删除[0,5]

如果输⼊的数组变成[[1,2], [2,4], [5,7]]呢,可以明显看出不需要删除任何数组

通过以上的例例⼦子是不不是发现了了什么?

所谓的贪⼼心算法就是找到局部的最优解,然后判断局部的最优解是否是全局的最优解?

这⾥是不是发现如果想删除最少的数组,只需要把跨度最⼤的数组删除就可以了。

也就是如果两个区间重合,删除其中一个区间的结尾最小的区间,因为区间结尾最小,说明以后删除的区间也就越少,也就是这⾥里里的贪⼼策略。

  1. 实现
/**
 * @param {number[][]} intervals * @return {number}
 */
export default (intervals) => {
  if (!intervals || intervals.length === 0 || intervals.length === 1) return 0;
  intervals.sort((a, b) => a[1] - b[1]);
  letmin = 0;
  for (let i = 1; i < intervals.length; ) {
    if (
      (intervals[i][0] < intervals[i - 1][1] &&
        intervals[i][0] >= intervals[i - 1][0]) ||
      intervals[i][0] < intervals[i - 1][0]
    ) {
      min++;
      intervals.splice(i, 1);
    } else {
      i++;
    }
  }
  return min;
};

时间复杂度O(nlgn)空间复杂度O(1)

/**
 * @param {number[][]} intervals * @return {number}
 */
export default (intervals) => {
  if (!intervals || intervals.length < 2) return 0;
  intervals.sort((a, b) => {
    if (a[0] === b[0]) {
      return a[1] - b[1];
    } else {
      return a[0] - b[0];
    }
  });
  let count = 0;
  for (let i = 1; i < intervals.length; ) {
    if (intervals[i][0] < intervals[i - 1][1]) {
      if (intervals[i][1] > intervals[i - 1][1]) {
        intervals.splice(i, 1);
      } else {
        intervals.splice(i - 1, 1);
      }
      count++;
    } else {
      i++;
    }
  }
  return count;
};

时间复杂度O(nlgn)空间复杂度O(1)