Interval - Summary


https://zhuanlan.zhihu.com/p/26657786
第一,排序。在一般情况下,按照interval的start升序排序,在必要情况下,对于start相同的interval,按照interval的end的降序排序。
第二,greedy。有时候是两个interval之间的greedy,有时候是一群interval之间的greedy。
第三,map。c++里的map,是用红黑树实现的,这里为了方便就叫map。当前两板斧都无法奏效的时候,可以试试这个。
然而排序常常和greedy一起使用。先排序,再greedy,是对付interval最常见的解法。当然这种套路远不仅限于interval类的题目。一个简单的题目是Leetcode 56. Merge Intervals.
Given a collection of intervals, merge all overlapping intervals. For example, Given [1,3],[2,6],[8,10],[15,18], return [1,6],[8,10],[15,18].
在排序之后,我们遍历数组,当两个interval有overlap的时候,我们取最小的start和最大的end。因为排序的结果,最小的start一定更早出现,所以我们只需要贪婪地去取最大的end就可以了。来看一下代码。
vector<Interval> merge(vector<Interval>& ins) {
        if (ins.empty()) return vector<Interval>{};
        sort(ins.begin(), ins.end(), [](const Interval & a, const Interval & b) -> bool{ return a.start < b.start; });
        vector<Interval> res {ins[0]};
        for (int i = 1; i < ins.size(); ++i) {
            if (res.back().end < ins[i].start) res.push_back(ins[i]);
            else    res.back().end = max(res.back().end, ins[i].end);
        }
        return res;
    }

再来看一个同样是sort+greedy的题目。Leetcode 57. Insert Interval.
Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). You may assume that the intervals were initially sorted according to their start times.
因为题目已经sort好了,所以我们只需要做greedy的部分就可以。先找到需要开始插入的地方,也就是找到第一个end在newInterval的start之后的interval。简单来说,插入的地方,newInterval的start需要在两个interval的end之间。找到了开始插入的地方,我们向后遍历。当有overlap的时候,我们贪婪地取newInterval和遍历经过的interval两者之间更小的start和更大的end。这就是newInterval merge之后的结果。最后我们再把不必要的interval给删掉。来看一下代码。
vector<Interval> insert(vector<Interval>& intervals, Interval newInterval) {
        int i=0;
        while(i<intervals.size() && intervals.at(i).end<newInterval.start) ++i;
        int m = i;
        while(i<intervals.size() && intervals.at(i).start<=newInterval.end){
            newInterval = Interval(min(intervals[i].start, newInterval.start), max(intervals[i].end, newInterval.end));
            ++i;
        }
        if(m < i)  intervals.erase(intervals.begin()+m, intervals.begin()+i);
        intervals.insert(intervals.begin()+m,newInterval);
        return intervals;
    }
再来一道类似的题目。Leetcode 435. Non-overlapping Intervals. 题目要求:
Given a collection of intervals, find the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
其实我本人在最早接触greedy算法的时候,学习的就是这个例子,可谓是非常经典。简单的逻辑就是,当有两个interval相互重叠的时候,我们一定要删去一个。那么删去哪一个呢?答案是删去end比较大的那个。这个可以用prove by contradiction轻松证明。来看一下借鉴自官方solution的code,其中pre记录的是目前为止保留的上一个interval。我这里把solution里面的compare function用lamda塞进去了。
int eraseOverlapIntervals(vector<Interval>& intervals) {
        sort(intervals.begin(), intervals.end(), [](const Interval& i1, const Interval& i2)->bool{return i1.start < i2.start;});
        int res = 0, pre = 0;
        for (int i = 1; i < intervals.size(); ++i) {
            if (intervals[i].start < intervals[pre].end) {
                res++;
                if (intervals[i].end < intervals[pre].end) pre = i;
            }
            else pre = i;
        }
        return res;
    }
我们目前为止所看到的greedy,都是两个interval之间的互相比较。那么有没有一群interval之间的比较,取greedy呢?有的。来看一道来自一亩三分地中的Facebook面经
给定一堆interval(如果我们管这个list叫IntervalList), 和一个target interval 我们的目标是去merge这些interval,让merge的结果能够『cover』这个target interval, 求这种merge所需的原interval的最少个数是多少
来看楼里的一个python solution。
def find_min_intervals(intervals, target): 
 intervals.sort() 
 res = 0 
 cur_target = target[0] 
 i = 0 
 max_step = 0 
 while i < len(intervals) and cur_target < target[1]: 
  while i < len(intervals) and intervals[i][0] <= cur_target: 
   max_step = max(max_step, intervals[i][1]) 
   i += 1 
  cur_target = max_step 
  res += 1 
 return res if cur_target >= target[1] else 0
这里有两点说明。第一,其实并不需要全部排序,只需要找出和target有overlap的interval进行排序。第二,关于greedy。在inner while loop里面,我们所需要找的,就是满足start比cur_target要小的interval中,end最大的那个。这样去最大的end可以让加入的这个interval,最有价值,延展到最远。一开始的cur_target是target的start,后面的cur_target就是上一个加入的interval的end(为了保持区间不中断)。
使用好sort和greedy,已经能解决大部分的interval的题目。然而,有些interval的题目,并不很典型,但是可以借助map来解决。map干的事情一般来说就是自动排序,方便查找。使用map来解决的interval问题,并没有太多的共同点。这里只是简单分享一下。
第一个简单粗暴的例子就是Leetcode 436. Find Right Interval.题目废话太多,简单来说就是对于每一个interval,返回第一个在它右边的interval。对于一个interval在自己右边的定义,就是它的start大于等于自己的end。
看一下官方的solution:
vector<int> findRightInterval(vector<Interval>& intervals) {
        map<int, int> hash;
        vector<int> res;
        int n = intervals.size();
        for (int i = 0; i < n; ++i)    hash[intervals[i].start] = i;
        for (auto in : intervals) {
            auto itr = hash.lower_bound(in.end);
            if (itr == hash.end()) res.push_back(-1);
            else res.push_back(itr->second);
        }
        return res;
    }
借助map自带的排序和lower_bound功能,这道题非常容易就解决了。
第二个简单粗暴的例子,Leetcode 253. Meeting Room II.
Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), find the minimum number of conference rooms required. For example, Given [[0, 30],[5, 10],[15, 20]], return 2.
其实就是寻找最多有多少个interval同时重叠。想象一下自己按照时间的发展来记录这些会议的出现,每有一个会议开始,就加一,每有一个会议结束,就减一。我们只要用map来记录这些会议的开始与结束就可以了。
int minMeetingRooms(vector<Interval>& intervals) {  
    map<int, int> mp;  
    for(auto x: intervals) {
        mp[x.start]++;
        mp[x.end]--;  
    }
    int res = 0, sum = 0;  
    for(auto x: mp) {
     sum += x.second;
        res = max(res, sum);  
    }
    return res;  
} 
最后一个例子是Leetcode 352. Data Stream as Disjoint Intervals. 描述和代码略复杂,就不复制了。在一个官方solution中提到两种方法,一种用vector维护出现的interval,线性时间插入新的interval,常数时间返回;另一种是用map维护出现的interval,O(lgn)时间插入新的interval,线性时间返回结果。具体情况具体分析咯。
总结一下,一般来说interval的题目无外乎insert, merge, overlap, contained等等。sort可以使这些interval达成某种性质,基于这种性质我们可以用greedy来寻找答案。当这个方法失灵的时候,可以考虑是否可以map是否会有帮助。
https://www.quora.com/What-is-the-problem-if-we-sort-the-intervals-according-to-their-finishing-time-like-the-interval-scheduling-problem-Why-is-it-necessary-to-sort-according-to-the-starting-time-in-the-interval-partitioning-problem
But I think what you mean is, why can't we sort on finishing time and then process the intervals from first to last, like in interval scheduling? And I think the reason is, for interval partitioning you need to keep track of which processes are currently running, or else you don't know which resources are free. So you need to know when they start to be able to account for that.


But for interval scheduling you actually don't need to know which processes are currently running. When a process finishes you can easily check whether the resource would have been available or not, and if it was, then clearly it should have processed that interval, so we can count it.


Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts