求多个区间的最大重合数量

这一题不是 leetcode 上的,是我在笔试中遇到的一个问题,在此总结一下。

问题描述:

小明选了 n 门在线课程,课程 i 开始和结束时间分别为 s[i]e[i]。小明可以一心多用,可以同时打开多少个播放器窗口来上课,问小明在上完这些课程的过程中,最多需要打开多少个窗口。

这个问题的本质是:给定了 n 个区间,问重合区间的数量最大是多少。

思路一

每门课开始的时候,我打开一个窗口,每门课结束的时候我关闭一个窗口。如果能够记录下每个时刻窗口的增量(可正可负),然后以时间顺序累加此增量,就可以得到每个时刻的窗口数量了。

如果时间范围比较小,而且比较稠密,可以使用数组以时间为下标来存储增量。否则,可以使用 tree map 来存储(因为后面要按照时间顺序遍历)。

struct Interval{
    int start;
    int end;
};

int max_overlapped_interval_count(const vector<Interval>& intervals){
    map<int, int> mp;
    for(const Interval& item: intervals){
        mp[item.start] += 1;
        mp[item.end] -= 1;
    }

    int res = 0;
    int n = 0;
    for(auto & item : mp){
        n += item.second; // 增量
        res = max(res, n);
    }
    return res;
}

思路二

如果把起点和终点都放到数轴上来观察,从数轴的最左边向右边遍历,遇到一个起点,我就需要打开一个播放器窗口,遇到一个终点我就关闭一个窗口。可见,在遍历数轴上所有点的过程中,可以得到最多需要打开的窗口数量。

enum POINT_TYPE{
    START, END
};

struct Point{
    Point(int v, POINT_TYPE t): val(v), type(t){}
    int val;
    POINT_TYPE type;
};

int max_overlapped_interval_count(const vector<Interval>& intervals){
    vector<Point> points;

    for(const Interval& item: intervals){
        points.emplace_back(item.start, START);
        points.emplace_back(item.end, END);
    }

    sort(points.begin(), points.end(), [](const Point& a, const Point& b){
        if(a.val < b.val){
            return true;
        }else if(a.val > b.val){
            return false;
        }else{
            return a.type == END && b.type == START; // 让终点排在前面
        }
    });

    int res = 0;
    int n = 0;
    for(auto & point : points){
        if(point.type == START){
            n += 1;
        }else{
            n -= 1;
        }
        res = max(res, n);
    }
    return res;
}

这里如果在同一个点上存在区间起点和终点,那么先处理终点,然后处理起点。也就是先关闭播放器窗口,在打开窗口,然后再更新打开窗口的最大数量。

另外 leetcode 上原题 生存人数 和本题其实是一样的,可以使用上面的思路去解答。不过这一题中在 sort 的时候,需要把起点放在前面。