區(qū)間重疊問(wèn)題(排序or邊界)

1.會(huì)議室(252-easy)

題目描述:給定一個(gè)會(huì)議時(shí)間安排的數(shù)組 intervals 瘦材,每個(gè)會(huì)議時(shí)間都會(huì)包括開始和結(jié)束的時(shí)間 intervals[i] = [starti, endi] 庆聘,請(qǐng)你判斷一個(gè)人是否能夠參加這里面的全部會(huì)議缭黔,即判斷是否存在重疊區(qū)域!

示例

輸入: intervals = [[0,30],[5,10],[15,20]]
輸出: false
解釋: 存在重疊區(qū)間,一個(gè)人在同一時(shí)刻只能參加一個(gè)會(huì)議。

思路:對(duì)每個(gè)會(huì)議時(shí)間按照開始時(shí)間排序(關(guān)鍵)侧漓。然后遍歷數(shù)組進(jìn)行判斷,如果前一個(gè)會(huì)議結(jié)束的時(shí)間大于后一個(gè)會(huì)議開始的時(shí)間(前一個(gè)還沒(méi)結(jié)束监氢,后一個(gè)就開始了)布蔗,則存在重疊區(qū)域。

代碼

public boolean canAttendMeetings(int[][] intervals) {
    if (intervals == null || intervals.length == 0) return false;
    /**       
    Arrays.sort(intervals, new Comparator<int[]>() {

            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });
  */
    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
    for (int i = 1; i < intervals.length; i++) {
        // 前一個(gè)結(jié)束時(shí)間大于后一個(gè)開始時(shí)間
        if (intervals[i][0] < intervals[i - 1][1]) {
            returtn false;
        }
    }
    return true;
}

拓展1:會(huì)議室II 253忙菠,給定一個(gè)會(huì)議時(shí)間安排的數(shù)組何鸡,每個(gè)會(huì)議時(shí)間都會(huì)包括開始和結(jié)束的時(shí)間 [[s1,e1],[s2,e2],…] (si < ei),為避免會(huì)議沖突牛欢,同時(shí)要考慮充分利用會(huì)議室資源骡男,請(qǐng)你計(jì)算至少需要多少間會(huì)議室,才能滿足這些會(huì)議安排傍睹。

示例

示例 1:
輸入: [[0, 30],[5, 10],[15, 20]]
輸出: 2

示例 2:
輸入: [[7,10],[2,4]]
輸出: 1

思路:有時(shí)間重疊的隔盛,肯定不能安排在一間會(huì)議室。還是需要先排序拾稳,這里按照會(huì)議的開始時(shí)間排序吮炕,這里使用小根堆(優(yōu)先級(jí)隊(duì)列)存儲(chǔ)會(huì)議的結(jié)束時(shí)間(堆頂為會(huì)議最早結(jié)束時(shí)間):

  • 如果另一場(chǎng)會(huì)議的開始時(shí)間小于當(dāng)前堆頂,說(shuō)明會(huì)議時(shí)間沖突访得,我們需要再單獨(dú)開一間(即當(dāng)前會(huì)議的結(jié)束時(shí)間作為新的堆頂)龙亲;
  • 否則陕凹,沒(méi)有時(shí)間沖突,等這場(chǎng)會(huì)議結(jié)束使用鳄炉。

代碼

import java.util.LinkedList;
class Solution {
    public int minMeetingRooms(int[][] intervals) {
        if(intervals == null || intervals.length == 0){
            return 0;
        }
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

        PriorityQueue<Integer> queue = new PriorityQueue<>((o1, o2) -> o1 - o2);
        queue.offer(intervals[0][1]);
        for(int i = 1; i < intervals.length; i++){
            if(intervals[i][0] >= queue.peek()){
                queue.poll();
            }
            queue.offer(intervals[i][1]);
        }
        return queue.size();
    }
}

拓展2:最多可以參加的會(huì)議數(shù)目1353杜耙,給你一個(gè)數(shù)組 events,其中 events[i] = [startDayi, endDayi] 拂盯,表示會(huì)議 i 開始于 startDayi 佑女,結(jié)束于 endDayi 。

你可以在滿足 startDayi <= d <= endDayi 中的任意一天 d 參加會(huì)議 i 谈竿。注意团驱,一天只能參加一個(gè)會(huì)議。請(qǐng)你返回你可以參加的 最大 會(huì)議數(shù)目空凸。

示例

示例 1:
輸入:events = [[1,2],[2,3],[3,4]]
輸出:3
解釋:你可以參加所有的三個(gè)會(huì)議嚎花。
安排會(huì)議的一種方案如上圖。
第 1 天參加第一個(gè)會(huì)議劫恒。
第 2 天參加第二個(gè)會(huì)議贩幻。
第 3 天參加第三個(gè)會(huì)議。

思路:本題還是排序两嘴,注意題意,我們不是要把一個(gè)會(huì)議的所有天都在族壳,只需要參加滿足會(huì)議(在開始和結(jié)束之間參加)的一天即可憔辫,比較簡(jiǎn)單的是利用set存儲(chǔ)已經(jīng)占用的天數(shù)(因?yàn)橥惶觳荒軈⒓觾蓚€(gè)會(huì)議),但是超時(shí)仿荆。贰您。。

可以使用優(yōu)先級(jí)隊(duì)列拢操,將這一天能參加的會(huì)議的結(jié)束時(shí)間全部入堆锦亦,會(huì)議的起始時(shí)間 == day,這一天一定能參加(注意按照會(huì)議的開始時(shí)間排序)令境。為什么是結(jié)束時(shí)間杠园?有點(diǎn)貪心思想,保證能參加最多的會(huì)議

  • 我們一天一天安排舔庶,首先刪除已經(jīng)結(jié)束的會(huì)議(出堆)抛蚁,此時(shí)堆頂這一天是我們可以參加的會(huì)議(一天只能參加一場(chǎng))!

代碼

class Solution {
    // public int maxEvents(int[][] events) {
    //     Arrays.sort(events, (o1, o2) -> o1[1] - o2[1]);
    //     Set<Integer> set = new HashSet<>();
    //     for (int[] event : events) {
    //         for (int i = event[0]; i <= event[1]; ++i) {
    //             if (!set.contains(i)) {
    //                 set.add(i);
    //                 break;
    //             }
    //         }
    //     }
    //     return set.size();
    // }

    public int maxEvents(int[][] events) {
        Arrays.sort(events, (o1, o2) -> o1[0] - o2[0]);
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        int i = 0;
        int day = 1;
        int ans = 0;
        int n = events.length;

        while (i < n || !pq.isEmpty()) {
            while (i < n && events[i][0] == day) {
                pq.offer(events[i][1]);
                i++;
            }

            while (!pq.isEmpty() && pq.peek() < day) {
                pq.poll();
            }

            if (!pq.isEmpty()) {
                pq.poll();
                ans++;
            }
            day++;
        }
        return ans;
    }
}

2.不重疊區(qū)間(435-medium)

題目描述:給定一個(gè)區(qū)間的集合惕橙,找到需要移除區(qū)間的最小數(shù)量瞧甩,使剩余區(qū)間互不重疊。

示例

輸入: [ [1,2], [2,3], [3,4], [1,3] ]
輸出: 1
解釋: 移除 [1,3] 后弥鹦,剩下的區(qū)間沒(méi)有重疊肚逸。

思路:為了保證移除最少的區(qū)間集合,即尋找最多的不重疊區(qū)間貪心策略:保證每個(gè)區(qū)間尾越小朦促,那么后邊預(yù)留的空間越大膝晾。

  • 可以通過(guò)對(duì)尾區(qū)間進(jìn)行排序,遍歷數(shù)組(記錄不重疊個(gè)數(shù)count)思灰,當(dāng)前頭小于上一個(gè)的尾玷犹,直接刪除(重疊),
  • 否則count++,更新最小的尾區(qū)間end洒疚。

ps:注意起始邊界處理歹颓,時(shí)間復(fù)雜度:O(nlogn)!

代碼

public int eraseOverlapIntervals(int[][] intervals) {
    if (intervals == null || intervals.length == 0) return 0;
    Arrays.sort(intervals, (a, b) -> a[1] - b[1]); //1.每個(gè)子區(qū)間尾排序
    int count = 1;
    int end = intervals[0][1]; 
    
    for (int i = 1; i < intervals.length; i++) {
        if (intervals[i][0] < end) continue;    //2.與之前區(qū)間重疊(即當(dāng)前區(qū)間的頭小于之前區(qū)間的尾油湖,刪除)
        end = intervals[i][1];
        count++;
    }
    return intervals.length - count;    //3.要?jiǎng)h除的區(qū)間數(shù)量
}

3.合并區(qū)間(56-medium)

題目描述:給出一個(gè)區(qū)間的集合巍扛,請(qǐng)合并所有重疊的區(qū)間。

示例

輸入: intervals = [[1,3],[2,6],[8,10],[15,18]]
輸出: [[1,6],[8,10],[15,18]]
解釋: 區(qū)間 [1,3] 和 [2,6] 重疊, 將它們合并為 [1,6].

輸入: intervals = [[1,4],[4,5]]
輸出: [[1,5]]
解釋: 區(qū)間 [1,4] 和 [4,5] 可被視為重疊區(qū)間乏德。

思路:實(shí)現(xiàn)合并區(qū)間肯定要進(jìn)行排序撤奸。采用每個(gè)子區(qū)間左邊界排序(右邊界也可),然后從左向右遍歷數(shù)組喊括。注意:

  • 若沒(méi)有重疊(數(shù)組為空/當(dāng)前區(qū)間的左邊界胧瓜,大于結(jié)果區(qū)間的右邊界),不需合并則加入結(jié)果郑什;
  • 否則更新右邊界生成新的區(qū)間加入結(jié)果府喳,更新結(jié)果區(qū)間的右邊界。

代碼

public int[][] merge(int[][] intervals) {
        //1.按照每個(gè)子區(qū)間端點(diǎn)(左端點(diǎn))進(jìn)行排序
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        int[][] ret = new int[intervals.length][2];
        int idx = -1;

        for (int[] interval : intervals) {
            //3.沒(méi)有重疊(數(shù)組為空/當(dāng)前區(qū)間左邊界大于結(jié)果區(qū)間右邊界)蘑拯,直接加入
            if (idx == -1 || interval[0] > ret[idx][1]) { 
                ret[++idx] = interval;
            } else {
                //4.有重疊钝满,合并區(qū)間(更新結(jié)果區(qū)間右邊界)
                ret[idx][1] = Math.max(interval[1], ret[idx][1]);  
            }
        }
        //ps:copyOf(int[] original, int newLength) 
        return Arrays.copyOf(ret, idx + 1);
    }

4.插入?yún)^(qū)間(57-medium)

題目描述:給出一個(gè)無(wú)重疊的按照區(qū)間起始端點(diǎn)排序的區(qū)間列表申窘。在列表中插入一個(gè)新的區(qū)間弯蚜,你需要確保列表中的區(qū)間仍然有序且不重疊(如果有必要的話,可以合并區(qū)間)剃法。

示例

輸入:intervals = [[1,3],[6,9]], newInterval = [2,5]
輸出:[[1,5],[6,9]]

輸入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
輸出:[[1,2],[3,10],[12,16]]
解釋:這是因?yàn)樾碌膮^(qū)間 [4,8] 與 [3,5],[6,7],[8,10] 重疊碎捺。

思路:因?yàn)樵紨?shù)組已經(jīng)排好序,并且不存在重疊玄窝,那么直接用索引i遍歷數(shù)組牵寺,索引idx記錄結(jié)果集索引,分三步走

  • 如果存在恩脂,將左邊與新區(qū)間不重疊的部分直接接入結(jié)果(沒(méi)影響的部分)帽氓;
  • 新區(qū)間與區(qū)間存在重疊,合并區(qū)間(更新新區(qū)間左右邊界)俩块,將更新后的新區(qū)間加入結(jié)果黎休;通俗一點(diǎn)說(shuō):新區(qū)間的左邊界<= 當(dāng)前上一個(gè)區(qū)間的右邊界浓领,>= 下一個(gè)區(qū)間的左邊界,我們需要將這三個(gè)區(qū)間合并(更新新區(qū)間的左右邊界势腮,加入結(jié)果)
  • 如果存在联贩,將右邊與新區(qū)間不重疊的部分直接接入結(jié)果(沒(méi)影響的部分);

代碼

public int[][] insert(int[][] intervals, int[] newInterval) {
    int[][] ret = new int[intervals.length + 1][2];
    int idx = 0, i = 0;
    //1.左邊不重疊區(qū)間
    while (i < intervals.length && newInterval[0] > intervals[i][1]) {
        ret[idx++] = intervals[i++];
    }
    //2.區(qū)間合并(更新新區(qū)間的左右邊界)
    while (i < intervals.length && newInterval[1] >= intervals[i][0]) {
        newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
        newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
        i++;
    }
    ret[idx++] = newInterval;
    //3.右邊不重疊區(qū)間
    while (i < intervals.length) {
        ret[idx++] = intervals[i++];
    }
    return Arrays.copyOf(ret, idx);
}

總結(jié)

總結(jié)上述四個(gè)題目捎拯,主要可以細(xì)分為兩類問(wèn)題泪幌。

涉及區(qū)間重疊問(wèn)題:

  • 區(qū)間是否重疊(T252)
  • 最多的的不重疊區(qū)間(T435)

涉及區(qū)間合并問(wèn)題:

  • 合并所有重疊區(qū)間(T56)
  • 合并插入過(guò)程中可能引入的重疊區(qū)間(T57)

其實(shí),上述問(wèn)題都需要對(duì)區(qū)間端點(diǎn)進(jìn)行排序署照,這里明確一點(diǎn)祸泪,不管是根據(jù)左端點(diǎn)排序還是右端點(diǎn)排序都是可以的。需要畫圖去看左右邊界情況建芙,比較直觀没隘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市禁荸,隨后出現(xiàn)的幾起案子右蒲,更是在濱河造成了極大的恐慌,老刑警劉巖赶熟,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瑰妄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡映砖,警方通過(guò)查閱死者的電腦和手機(jī)翰撑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)啊央,“玉大人,你說(shuō)我怎么就攤上這事涨醋」霞ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵浴骂,是天一觀的道長(zhǎng)乓土。 經(jīng)常有香客問(wèn)我,道長(zhǎng)溯警,這世上最難降的妖魔是什么趣苏? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮梯轻,結(jié)果婚禮上食磕,老公的妹妹穿的比我還像新娘。我一直安慰自己喳挑,他們只是感情好彬伦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布滔悉。 她就那樣靜靜地躺著,像睡著了一般单绑。 火紅的嫁衣襯著肌膚如雪回官。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天搂橙,我揣著相機(jī)與錄音歉提,去河邊找鬼。 笑死区转,一個(gè)胖子當(dāng)著我的面吹牛苔巨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜗帜,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼恋拷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了厅缺?” 一聲冷哼從身側(cè)響起蔬顾,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎湘捎,沒(méi)想到半個(gè)月后诀豁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窥妇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年舷胜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片活翩。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡烹骨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出材泄,到底是詐尸還是另有隱情沮焕,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布拉宗,位于F島的核電站峦树,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏旦事。R本人自食惡果不足惜魁巩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姐浮。 院中可真熱鬧谷遂,春花似錦、人聲如沸单料。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至白对,卻和暖如春掠廓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背甩恼。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工蟀瞧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人条摸。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓悦污,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親钉蒲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子切端,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容