diff --git a/dynamic_programming/Interval Problem (I) Interval Scheduling.md b/dynamic_programming/Interval Problem (I) Interval Scheduling.md new file mode 100644 index 0000000..1681b81 --- /dev/null +++ b/dynamic_programming/Interval Problem (I) Interval Scheduling.md @@ -0,0 +1,187 @@ +# Interval Problem (I): Interval Scheduling + +What is the greedy algorithm? It can be regarded as a special case of dynamic programming (DP). Compared with DP, using greedy algorithm need to meet more conditions, such as the greedy choosing property, but show more efficiency. + +For example, supposed that a certain algorithm using enumeration method needs exponential time, if the overlapping subproblems can be solved by DP, then polynomial time is available. Furthermore, if it meets the greedy choosing property, the time complexity can be reduced to a linear level. + +So what is the greedy choosing property? Easily speaking, if the final global optimum can be satisfied by several local-optimal steps, then we call the algorithm have the greedy choosing property. And also we should remind that it's a special property, only a part of problem have characteristic like this. + +For example, if you can choose ten banknotes from 100 given banknotes, how to get banknotes with the highest values in total? Clearly, choosing the banknote with the highest value in the rest each time would bring about the global optimum. + +### First Part: Problem Restatement + +This article will solve a classical greedy algorithm problem: Interval Scheduling. Given a series of closed intervals `[start, end]` , you should design an algorithm to compute the number of maximum subsets without any overlapping. + +```java +int intervalSchedule(int[][] intvs) {} +``` + +For example,`intvs = [[1,3], [2,4], [3,6]]`, the interval set have 2 subsets without any overlapping at most, `[[1,3], [3,6]]` , so your algorithm should return 2 as the result. Note that intervals with the same border doesn't meet the condition. + +This problem is widely used in our daily life. For example, you get several activities today, each activity can be represented by its starting time and its ending time with interval`[start, end]` . Clearly you can't attend 2 activities at the same time, so this problem can be change into a question about how to find the maximum subsets without any time overlapping. + +### Second Part: Greedy Algorithm + +For this problem, there are some potential thought, but none of them could lead to the correct answer. + +① Choosing the interval with the earliest starting time. + +There maybe exists some intervals appear very early, but they can also be with long duration, which make us missing up some short intervals. + +② Choosing the interval with the shortest duration. + +③ Choosing the interval with the shortest duration. + +It's easy to raise counterexample to these solution. + +The correct thought can be very easy, which can be devided into three parts: + +① Choosing a interval 'x', which has the earliest ending time among all the current intervals, from the interval set 'intvs'. + +② Delete all invertals intersecting with 'x'. + +③ Repecting ① and ②, until intvs gets empty. These 'x' selected before are the subsets meeting the conditions. + +Now when we change this thought into algorithm, it's more convenient to implement ① and ② with a ascending sorting by `end` for each interval. + +【Explanations for the chinese in the picture】 + +【索引:index】【按end排序,sorting by end】【选择区间x:choosing the interval x】 + +【更新x:updating x】【去除x的重叠区间:delecting the overlapping intervals with x】 + +【得到结果:achieve the results】 + + + +![1](../pictures/interval/1.gif) + +Now we implement our algorithm. For the step one, since we ordered `end` in advance, then it's easy to choose the 'x'. The key point is how to delect the intervals intersecting with 'x' and choose the new 'x' for the next loop. + +Thanks to the ordering, it's not difficult to find out all the interval intersecting with 'x' will contain the `end` of 'x'. Namely, if a interval doesn't contain the ending point of 'x', then its `start` must bigger or equal to the `end` of 'x'. + +【Comments for the chinese in the picture】 + +![2](../pictures/interval/2.jpg) + +Here is the code: + +```java +public int intervalSchedule(int[][] intvs) { + if (intvs.length == 0) return 0; + // ascending sorting by end + Arrays.sort(intvs, new Comparator() { + public int compare(int[] a, int[] b) { + return a[1] - b[1]; + } + }); + // at least have one interval without intersection + int count = 1; + // after sorting, the first interval is x + int x_end = intvs[0][1]; + for (int[] interval : intvs) { + int start = interval[0]; + if (start >= x_end) { + // get the next selected interval + count++; + x_end = interval[1]; + } + } + return count; +} +``` + +### Third Part: Example for the Application + +Now we will take some problem from leetcode to apply the interval scheduling algorithm. + +【Leetcode 435】Given a collection of intervals, find the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping. + +**Example 1:** + +``` +Input: [[1,2],[2,3],[3,4],[1,3]] +Output: 1 +Explanation: [1,3] can be removed and the rest of intervals are non-overlapping. +``` + +**Example 2:** + +``` +Input: [[1,2],[1,2],[1,2]] +Output: 2 +Explanation: You need to remove two [1,2] to make the rest of intervals non-overlapping. +``` + +**Example 3:** + +``` +Input: [[1,2],[2,3]] +Output: 0 +Explanation: You don't need to remove any of the intervals since they're already non-overlapping. +``` + +**Note:** + +1. You may assume the interval's end point is always bigger than its start point. +2. Intervals like [1,2] and [2,3] have borders "touching" but they don't overlap each other. + +Since we are able to compute the original case, it's easy to finish this case by achieving the intervals which need to be removed. + +```java +int eraseOverlapIntervals(int[][] intervals) { + int n = intervals.length; + return n - intervalSchedule(intervals); +} +``` + +【Leetcode 452】Minimum Number of Arrows to Burst Balloons + +There are a number of spherical balloons spread in two-dimensional space. For each balloon, provided input is the start and end coordinates of the horizontal diameter. Since it's horizontal, y-coordinates don't matter and hence the x-coordinates of start and end of the diameter suffice. Start is always smaller than end. There will be at most 104 balloons. + +An arrow can be shot up exactly vertically from different points along the x-axis. A balloon with xstart and xend bursts by an arrow shot at x if xstart ≤ x ≤ xend. There is no limit to the number of arrows that can be shot. An arrow once shot keeps travelling up infinitely. The problem is to find the minimum number of arrows that must be shot to burst all balloons. + +**Example:** + +``` +Input: +[[10,16], [2,8], [1,6], [7,12]] + +Output: +2 + +Explanation: +One way is to shoot one arrow for example at x = 6 (bursting the balloons [2,8] and [1,6]) and another arrow at x = 11 (bursting the other two balloons). +``` + +Actually, it's not difficult to find that this question is the same as the interval scheduling algorithm. If there are n intervals without overlapping at most, then at least n arrows which get throw all the intervals are needed. + +![3](../pictures/interval/3.jpg) + +There still a little difference: in the interval schedule, the same border will not be regarded as overlapping, but it counts in this problem. + +【Explanations for the chinese in the picture】 + +【射气球:shooting the balballoon】 + +![4](../pictures/interval/4.jpg) + +Therefore, we can get the answer to this problem with only a little change. + +```java +int findMinArrowShots(int[][] intvs) { + // ... + + for (int[] interval : intvs) { + int start = interval[0]; + // Change >= into > + if (start > x_end) { + count++; + x_end = interval[1]; + } + } + return count; +} +``` + +It's not difficult to understand why it ought be done like that: 'x' should not be updated when `start == x_end` , since the same border is also regarded as overlapping. \ No newline at end of file diff --git a/think_like_computer/Interval Problem (II) Interval Merging.md b/think_like_computer/Interval Problem (II) Interval Merging.md new file mode 100644 index 0000000..1b4adc4 --- /dev/null +++ b/think_like_computer/Interval Problem (II) Interval Merging.md @@ -0,0 +1,86 @@ +# Interval Problem (II): Interval Merging + +In the "Interval Scheduling: Greedy Algorithm", we use greedy algorithm to solve the interval scheduling problem, which means, given a lot of intervals, finding out the maximum subset without any overlapping. + +Actually, there are many other relating problems about interval itself. Now, we will talk about the "Merge Interval Problem". + +【Leetcode 56】Merge Intervals + +Given a collection of intervals, merge all overlapping intervals. + +**Example 1:** + +``` +Input: [[1,3],[2,6],[8,10],[15,18]] +Output: [[1,6],[8,10],[15,18]] +Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6]. +``` + +**Example 2:** + +``` +Input: [[1,4],[4,5]] +Output: [[1,5]] +Explanation: Intervals [1,4] and [4,5] are considered overlapping. +``` + +**NOTE:** input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature. + +The general thought for solving interval problems is observing regular patterns after the sorting process. + +### First Part: Thought + +A certain interval can be defined as`[start, end]`, the interval scheduling in the last article states the sorting process need to be done by `end`. But for the merging problem, both sorting with the `end` or `start` are acceptable. For the clear purpose, we choose sorting by `start` . + +【Explanations for chinese in the picture】 + +【按start排序:sorting by start】【索引:index】 + +![1](../pictures/mergeInterval/1.jpg) + +Clearly, for the merging result `x`, `x.start`must have the smallest `start` in these intersected intervals, and `x.end` must have the largest `end` in these intersected intervals as well. + +![2](../pictures/mergeInterval/2.jpg) + +Since ordered, `x.start` is easy to achieve, and computing `x.end` is also not difficult as well, which can take an analogy of searching the max number in a certain array. + +```java +int max_ele = arr[0]; +for (int i = 1; i < arr.length; i++) + max_ele = max(max_ele, arr[i]); +return max_ele; +``` + +### Second Part: Code + +```python +# intervals like [[1,3],[2,6]...] +def merge(intervals): + if not intervals: return [] + # ascending sorting by start + intervals.sort(key=lambda intv: intv[0]) + res = [] + res.append(intervals[0]) + + for i in range(1, len(intervals)): + curr = intervals[i] + # quote of the last element in res + last = res[-1] + if curr[0] <= last[1]: + # find the biggest end + last[1] = max(last[1], curr[1]) + else: + # address next interval need to be merged + res.append(curr) + return res +``` + +It will be illustrated more clearly by the follow gif. + +![3](../pictures/mergeInterval/3.gif) + +So far, the Interval Merging Problem have been solved. + +The End. Hope this article can help you! + +![labuladong](../pictures/labuladong.jpg) diff --git a/think_like_computer/Interval Problem (III) Interval Intersection.md b/think_like_computer/Interval Problem (III) Interval Intersection.md new file mode 100644 index 0000000..35b30f4 --- /dev/null +++ b/think_like_computer/Interval Problem (III) Interval Intersection.md @@ -0,0 +1,128 @@ +# Interval Problem (III): Interval Intersection + +This is the third article about the interval problem, and the last two articles respectively introduce the interval scheduling problem and the interval merging problem. Now, we will talk about the topic about how to find out interval intersection from two set of intervals efficiently. + +【Leetcode 986】Interval List Intersections + +Given two lists of **closed** intervals, each list of intervals is pairwise disjoint and in sorted order. + +Return the intersection of these two interval lists. + +*(Formally, a closed interval [a, b] (with a <= b) denotes the set of real numbers xwith a <= x <= b. The intersection of two closed intervals is a set of real numbers that is either empty, or can be represented as a closed interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3].)* + +**Example 1:** + +**![img](https://assets.leetcode.com/uploads/2019/01/30/interval1.png)** + +``` +Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]] +Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]] +Reminder: The inputs and the desired output are lists of Interval objects, and not arrays or lists. +``` + +**Note:** + +1. `0 <= A.length < 1000` +2. `0 <= B.length < 1000` +3. `0 <= A[i].start, A[i].end, B[i].start, B[i].end < 10^9` + +**NOTE:** input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature. + +### Part One: Thought + +The general thought for interval problems is sorting first. Since question states that it has been ordered, then we can use two pointers to find out the intersections. + +Here is the code: + +```python +# A, B like [[0,2],[5,10]...] +def intervalIntersection(A, B): + i, j = 0, 0 + res = [] + while i < len(A) and j < len(B): + # ... + j += 1 + i += 1 + return res +``` + +Next, we will analyze all the situations or cases. + +First, for two intervals, we use `[a1,a2]` and `[b1,b2]` to represent two intervals in the `A` and `B` respectively. So, let us find out how to make these two intervals don't have intersections. + +![](../pictures/intersection/1.jpg) + +It can be written in code like this: + +``` +if b2 < a1 or a2 < b1: + [a1,a2] and [b1,b2] don't exist intersection +``` + +Then, what conditions should be met when two intervals exist intersection? + +The negative proposition of the above logic is the condition. + +```python +# get a inverse direction of the sign of inequality, and change 'or' into 'and' +if b2 >= a1 and a2 >= b1: + [a1,a2] and [b1,b2] exist intersection +``` + +Then, we enumerate all the situation that two intervals exist intersection. + +![](../pictures/intersection/2.jpg) + +It seems very simple: only four situation. exist. Then we should think about what's the common feather among these situations. + +![](../pictures/intersection/3.jpg) + +We surprisingly observe that the intersection of intervals get regular pattern. If the intersection is `[c1,c2]` then `c1=max(a1,b1)`,`c2=min(a2,b2)`! Thus this observation is the key point of finding out the interaction. Now we make our code get further. + +```python +while i < len(A) and j < len(B): + a1, a2 = A[i][0], A[i][1] + b1, b2 = B[j][0], B[j][1] + if b2 >= a1 and a2 >= b1: + res.append([max(a1, b1), min(a2, b2)]) + # ... +``` + +Last step, it's surely that the pointer `i` and `j` will go forward, but when? + +![](../pictures/intersection/4.gif) + +It's more understandable throught the gif that whether going forward only depends on the relationship between `a2` and`b2`. + +```python +while i < len(A) and j < len(B): + # ... + if b2 < a2: + j += 1 + else: + i += 1 +``` + +### Second Part: Code + +```python +# A, B like [[0,2],[5,10]...] +def intervalIntersection(A, B): + i, j = 0, 0 # double pointers + res = [] + while i < len(A) and j < len(B): + a1, a2 = A[i][0], A[i][1] + b1, b2 = B[j][0], B[j][1] + # two intervals have intersection + if b2 >= a1 and a2 >= b1: + # compute the intersection and add it into res + res.append([max(a1, b1), min(a2, b2)]) + # Pointer go forward + if b2 < a2: j += 1 + else: i += 1 + return res +``` + +To give a brief summary, although the problem concerning intervals seems to be complicated, we can still use simple code to finish the task by observe common features between different situation. + +![labuladong](../pictures/labuladong.jpg)