about Merge Intervals 1
my Merge Intervalsleet code list leetcode
- Insert Interval 57
- Employee Free Time 759
- Merge Intervals 56
- The Skyline Problem 218
- Non-overlapping Intervals 435
- Minimum Number of Arrows to Burst Balloons 452
- My Calendar I 729
- My Calendar II 731
- My Calendar III 732
- Describe the Painting 1943
- Falling Squares 699
- Task Scheduler 621
区间合并模式是一个用来处理有区间重叠的很高效的技术。在设计到区间的很多问题中,通常咱们需要要么判断是否有重叠,要么合并区间,如果他们重叠的话。这个模式是这么起作用的:
怎么识别啥时候用合并区间模式呀?
当你需要产生一堆相互之间没有交集的区间的时候 当你听到重叠区间的时候 经典题目:
Insert Interval 57
You are given an array of non-overlapping intervals intervals where intervals[i] = [starti, endi] represent the start and the end of the ith interval and intervals is sorted in ascending order by starti. You are also given an interval newInterval = [start, end] that represents the start and end of another interval.
Insert newInterval into intervals such that intervals is still sorted in ascending order by starti and intervals still does not have any overlapping intervals (merge overlapping intervals if necessary).
Return intervals after the insertion.
Example 1:
Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]
input:[ [1, 2] [3, 5] [6, 7] [8, 10] [12, 16] ]
newInterval[4, 8]
[1, 2]中 2 点结束, 早于 4 点开始, 所以 res 中添加 [1, 2]
while (i < intervals.length() && intervals[i][1] < newInterval[0]) { res.add(new int[]{intervals[i][0], intervals[i][1]}); }
res: [1, 2] []
[3, 5]中 和 4 发生了 overlap, 跳出循环
while (i < intervals.length() && intervals[i][0] <= newInterval[1]) { newInterval[0] = Math.min(intervals[i][0], new Interval[0]); // newInterval[3, 8], [3, 8], [3, 10] newInterval[1] = Math.max(intervals[i][1], new Interval[1]); i++; }
更新完 newInterval, 加入 res
[12, 16]中 8 结束的时间小于 12 开始的时间,所以肯定没交集
☕ 推荐山景城一姐 youtube :coffee:
public int[][] insert(int[][] intervals, int[] newInterval) {
List<int[]> res = new ArrayList<>();
int i = 0;
while (i < intervals.length && intervals[i][1] < newInterval[0]) {
res.add(intervals[i]);
i++;
}
while (i < intervals.length && intervals[i][0] <= newInterval[1]) {
newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
i++;
}
res.add(newInterval);
while (i < intervals.length) {
res.add(intervals[i]);
i++;
}
return res.toArray(new int[res.size()][]);
}
Employee Free Time 759
public class Solution {
public List<Interval> employeeFreeTime(List<List<Interval>> schedule) {
List<Interval> res = new ArrayList<>();
List<int[]> list = new ArrayList<>();
for (List<Interval> intervals : schedule) {
for (Interval interval : intervals) {
list.add(new int[]{interval.start, 0});
list.add(new int[]{interval.end, 1});
}
}
list.sort((p1, p2) -> p1[0] != p2[0] ? Integer.compare(p1[0], p2[0]) : Integer.compare(p1[1], p2[1]));
int count = 0;
boolean begin = false;
for (int[] pair : list) {
if (pair[1] == 0) {
count++;
} else {
count--;
}
if (count == 0) {
res.add(new Interval(pair[0], pair[0]));
begin = true;
} else if (begin) {
res.get(res.size() - 1).end = pair[0];
begin = false;
}
}
res.remove(res.size() - 1);
return res;
}
}
class Interval {
public int start;
public int end;
public Interval() {
}
public Interval(int start, int end) {
this.start = start;
this.end = end;
}
}
Merge Intervals 56
Given an array of intervals where intervals[i] = [starti, endi], merge all overlapping intervals, and return an array of the non-overlapping intervals that cover all the intervals in the input.
Example 1:
Input: intervals = [[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:
Constraints:
1 <= intervals.length <= 104
intervals[i].length == 2
0 <= starti <= endi <= 104
时间复杂度 O ( n log n ) ,空间 O ( n )
public int[][] merge(int[][] intervals) {
if (intervals == null || intervals.length == 0) {
return intervals;
}
Arrays.sort(intervals, (int[]a, int[]b) -> a[0] - b[0]);
List<int[]> res = new ArrayList<>();
res.add(intervals[0]); // [1, 3] ->
for (int i = 1; i < intervals.length; i++) { // start from 1
int[] end = res.get(res.size() - 1); // end = [1, 3] vs intervals[1][0]
if (end[1] >= intervals[i][0]) { // 3 > 2
end[1] = Math.max(end[1], intervals[i][1]); // end = [1, 6]
} else {
res.add(intervals[i]);
}
}
return res.toArray(new int[res.size()][2]);
}
The Skyline Problem 218
A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Given the locations and heights of all the buildings, return the skyline formed by these buildings collectively.
The geometric information of each building is given in the array buildings where buildings[i] = [lefti, righti, heighti]:
lefti is the x coordinate of the left edge of the ith building.
righti is the x coordinate of the right edge of the ith building.
heighti is the height of the ith building.
You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0.
The skyline should be represented as a list of "key points" sorted by their x-coordinate in the form [[x1,y1],[x2,y2],...]. Each key point is the left endpoint of some horizontal segment in the skyline except the last point in the list, which always has a y-coordinate 0 and is used to mark the skyline's termination where the rightmost building ends. Any ground between the leftmost and rightmost buildings should be part of the skyline's contour.
Note: There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...,[2 3],[4 5],[7 5],[11 5],[12 7],...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...,[2 3],[4 5],[12 7],...]
Example 1:
Input: buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
Output: [[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]]
Explanation:
Figure A shows the buildings of the input.
Figure B shows the skyline formed by those buildings. The red points in figure B represent the key points in the output list.
[[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
public List<List<Integer>> getSkyline(int[][] buildings) {
List<List<Integer>> res = new ArrayList<>();
/*
[L, R, H] -> [L, R] [R, H]
[[2,9,10], [2,-10], [9,10]
[3,7,15], [3,-15], [7,15]
[5,12,12], [5,-12], [12,12]
[15,20,10],
[19,24,8]]
用正负区分是左端点还是右端点
然后排列他们, 但是高度是按降序排列处理
[2,-10],
[3,-15],
[5,-12],
[7,15]
[9,10]
*/
List<int[]> buildLines = new ArrayList<>();
for (int[] points : buildings) {
buildLines.add(new int[]{points[0], -points[2]});
buildLines.add(new int[]{points[1], points[2]});
}
Collections.sort(buildLines, (a, b) -> {
if (a[0] != b[0]) {
return a[0] - b[0];
} else {
return a[1] - b[1];
}
});
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
maxHeap.add(0);
int preHightest = 0;
for (int[] points : buildLines) {
if (points[1] < 0) { // h[1] < 0, that means it meets a new building, so add it to pq
maxHeap.add(-points[1]);
} else {
maxHeap.remove(points[1]); // h[1] >=0, that means it has reached the end of the building, so remove it from pq
}
// the current max height in all encountered buildings
int curHeight = maxHeap.peek();
// if the max height is different from the previous one, that means a critical point is met, add to result list
if (curHeight != preHightest) {
res.add(Arrays.asList(points[0], curHeight));
preHightest = curHeight;
}
}
return res;
}
Time complexity = O(n^2)
O(n) space
优先队列的 remove 操作需要先经过 O(n)O(n) 的复杂度进行查找,再通过 O(\log{n})O(logn) 的复杂度进行删除。因此整个 remove 操作的复杂度是 O(n)O(n) 的,这导致了我们算法整体复杂度为 O(n^2)
☕☕☕
O(nlogn) time
首先考虑,如果只给一个建筑 [x, y, h],那么答案是多少?
很明显输出的解将会是 [[x, h], [y, 0]],也就是左上角和右下角坐标。
接下来考虑,如果有建筑 A B C D E,我们知道了建筑 A B C 输出的解和 D E 输出的解,那么怎么把这两组解合并,得到 A B C D E 输出的解。
{{0,2,3}, {2,5,3}}
left: [[0, 3], [2, 0]] //左上角, 右下
right: [[2, 3], [5, 0]] //左上角, 右下
第一轮, left:[0, 3], right:[2, 3]
x1 = 0, x2 = 2
leftH = temp[1]; // 3
res.add[0, 3]
第二轮, left:[2, 0], right:[2, 3]
x1 = 2, x2 = 2
leftH = temp[1]; // 0 rightH = right.pollFirst()[1]; // 3
res 不加
第三轮, right:[5, 0]
x = 5
rightH = right.pollFirst()[1]; // 0
res.add[5, 0]
public List<List<Integer>> getSkyline(int[][] buildings) {
return merge(buildings, 0, buildings.length - 1);
}
private LinkedList<int[]> merge(int[] buildings, int lo, int hi) {
LinkedList<int[]> res = new LinkedList<>();
if (lo > hi) {
return res;
} else if (lo == hi) {
res.add(new int[]{buildings[lo][0], buildings[lo][2]}); // [[x, h], [y, 0]]
res.add(new int[]{buildings[lo][1], 0});
return res;
}
int mid = lo + (hi - lo)/2;
LinkedList<int[]> left = merge(buildings, lo, mid); // left: [[0, 3], [2, 0]]
LinkedList<int[]> right = merge(buildings, mid + 1, hi); // right: [[2, 3], [5, 0]]
int leftH = 0, rightH = 0;
while(!left.isEmpty() || !right.isEmpty()) {
long x1 = left.isEmpty()? Long.MAX_VALUE: left.peekFirst()[0]; // 0
long x2 = right.isEmpty()? Long.MAX_VALUE: right.peekFirst()[0]; // 2
int x = 0;
if(x1 < x2) {
int[] temp = left.pollFirst(); // [0, 3]
x = temp[0]; // 0
leftH = temp[1]; // 3
} else if(x1 > x2) {
int[] temp = right.pollFirst();
x = temp[0];
rightH = temp[1];
} else {
x = left.peekFirst()[0];
leftH = left.pollFirst()[1];
rightH = right.pollFirst()[1];
}
int h = Math.max(leftH, rightH); // 3
if(res.isEmpty() || h != res.peekLast()[1]) {
res.add(new int[]{x, h}); // [0, 3]
}
}
return res;
}
Non-overlapping Intervals 435
Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Example 1:
Input: intervals = [[1,2],[2,3],[3,4],[1,3]]
Output: 1
Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping.
public int eraseOverlapIntervals(int[][] intervals) {
if(intervals == null || intervals.length == 0) return 0;
Arrays.sort(intervals, (a, b) -> a[1] - b[1]);
int end = intervals[0][1];
int count = 1;
for(int i = 1; i < intervals.length; i++){
if(intervals[i][0] >= end){
end = intervals[i][1];
count++;
}
}
return intervals.length - count;
}
// int end = Integer.MIN_VALUE;
// int count = 0;
// for (Interval interval : intervals) {
// if (interval.start >= end) end = interval.end;
// else count++;
// }
// return count;
// }
public int eraseOverlapIntervals(int[][] intervals) {
[[1,100],[11,22],[1,11],[2,12]]
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
Arrays.sort(intervals, (a, b) -> a[1] - b[1]); //[1,11],[2,12], [11,22], [1,100]
int end = intervals[0][1];
int len = 1;
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] >= end) { // 将 11 与2, 22,数组[0]z对比
end = intervals[i][1];
len++;
}
}
return intervals.length - len;
}
}
public int eraseOverlapIntervals(int[][] intervals) {
if (intervals == null || intervals.length == 0) return 0;
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
int prevEnd = intervals[0][1];
int count = 0;
for (int i = 1; i < intervals.length; i++) {
if (prevEnd > intervals[i][0]) {
count++;
prevEnd = Math.min(intervals[i][1], prevEnd); // 把 11 和100 取最小的
} else {
prevEnd = intervals[i][1];
}
}
return count;
}
Minimum Number of Arrows to Burst Balloons 452
There are some spherical balloons taped onto a flat wall that represents the XY-plane. The balloons are represented as a 2D integer array points where points[i] = [xstart, xend] denotes a balloon whose horizontal diameter stretches between xstart and xend. You do not know the exact y-coordinates of the balloons.
Arrows can be shot up directly vertically (in the positive y-direction) from different points along the x-axis. A balloon with xstart and xend is burst by an arrow shot at x if xstart <= x <= xend. There is no limit to the number of arrows that can be shot. A shot arrow keeps traveling up infinitely, bursting any balloons in its path.
Given the array points, return the minimum number of arrows that must be shot to burst all balloons.
Example 1:
Input: points = [[10,16],[2,8],[1,6],[7,12]]
Output: 2
Explanation: The balloons can be burst by 2 arrows:
- Shoot an arrow at x = 6, bursting the balloons [2,8] and [1,6].
- Shoot an arrow at x = 11, bursting the balloons [10,16] and [7,12].
wrong answer, sort with a[0]
public int findMinArrowShots(int[][] points) {
int count = 1;
Arrays.sort(points, (a, b) -> Integer.compare(a[0],b[0]));
int tmp = points[0][1]; // 6
for (int i = 1; i < points.length; i++) {
if (tmp < points[i][0]) {
count++;
tmp = points[i][1];
}
}
return count;
}
[[3,9],[7,12],[3,8],[6,8],[9,10],[2,9],[0,9],[3,9],[0,6],[2,8]]
按我的结果
[0,9] [0,6][2,9] [2,8] [3,9] [3,8] [3,9][6,8][7,12][9,10]
tmp = 9, 所以我只会用一个, 然后 [3,8],这个 case 我就射不到
public int findMinArrowShots(int[][] points) {
int count = 1;
Arrays.sort(points, (a, b) -> Integer.compare(a[1],b[1]));
int tmp = points[0][1]; // 6
for (int i = 1; i < points.length; i++) {
if (tmp < points[i][0]) {
count++;
tmp = points[i][1];
}
}
return count;
}
My Calendar I 729
You are implementing a program to use as your calendar. We can add a new event if adding the event will not cause a double booking.
A double booking happens when two events have some non-empty intersection (i.e., some moment is common to both events.).
The event can be represented as a pair of integers start and end that represents a booking on the half-open interval [start, end), the range of real numbers x such that start <= x < end.
Implement the MyCalendar class:
MyCalendar() Initializes the calendar object.
boolean book(int start, int end) Returns true if the event can be added to the calendar successfully without causing a double booking. Otherwise, return false and do not add the event to the calendar.
Example 1:
Input
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
Output
[null, true, false, true]
- a0|——————–|a1
. b0|——b1
- a0|——————–|a1
. b0|—————–b1
- a0|———|a1
. b0|————-b1
错误的答案, 我一开始想的是只有为 true 的数据往后插,可是忽略了一个条件就是插头插中间了,那就不能按我的只对比尾巴的那个
public boolean book(int start, int end) {
if (res.size() == 0) {
res.add(new int[]{start, end});
return true;
}
int[] tmp = res.get(res.size() - 1);
if (Math.max(tmp[0], start) < Math.min(tmp[1], end)) {
return false;
}
res.add(new int[]{start, end});
return true;
}
[“MyCalendar”,“book”,“book”,“book”,“book”,“book”,“book”,“book”,“book”,“book”,“book”] [[],[47,50],[33,41],[39,45],[33,42],[25,32],[26,35],[19,25],[3,8],[8,13],[18,27]]
我的
[null,true,true,false,false,true,false,true,true,true,true]
[null,true,true,false,false,true,false,true,true,true,false]
res = new ArrayList<>();
}
public boolean book(int start, int end) {
for (int[] tmp : res) {
if (Math.max(tmp[0], start) < Math.min(tmp[1], end)) {
return false;
}
}
res.add(new int[]{start, end});
return true;
}
每次都要遍历一边,所以按我的想法让它排序好, map 来建立起始时间和结束时间的映射,map 会按照起始时间进行自动排序。然后对于新进来的区间,我们在已有区间中查找第一个不小于新入区间的起始时间的区间, 当前区间起始时间小于新入区间结束时间的话返回 false, 如果前一个区间的结束时间大于新入区间的起始时间的话,返回 false。否则就建立新的映射,返回 true 即可
public boolean book(int start, int end) {
Map.Entry<Integer, Integer> lowerEntry = map.lowerEntry(end); // start1 == end will not be seen as overlap
if(lowerEntry != null && lowerEntry.getValue() > start) { // or use or the condition as authors posts
return false;
}
map.put(start, end);
return true;
}
My Calendar II 731
ou are implementing a program to use as your calendar. We can add a new event if adding the event will not cause a triple booking.
A triple booking happens when three events have some non-empty intersection (i.e., some moment is common to all the three events.).
The event can be represented as a pair of integers start and end that represents a booking on the half-open interval [start, end), the range of real numbers x such that start <= x < end.
Implement the MyCalendarTwo class:
MyCalendarTwo() Initializes the calendar object.
boolean book(int start, int end) Returns true if the event can be added to the calendar successfully without causing a triple booking. Otherwise, return false and do not add the event to the calendar.
Example 1:
Input
["MyCalendarTwo", "book", "book", "book", "book", "book", "book"]
[[], [10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]]
Output
[null, true, true, true, false, true, true]
class MyCalendarTwo {
List<int[]> books = new ArrayList<>();
public boolean book(int start, int end) {
MyCalendar overlaps = new MyCalendar();
for (int[] b : books)
if (Math.max(b[0], start) < Math.min(b[1], end)) // overlap exist
if (!overlaps.book(Math.max(b[0], start), Math.min(b[1], end))) return false; // overlaps overlapped
books.add(new int[]{ start, end });
return true;
}
private static class MyCalendar {
List<int[]> books = new ArrayList<>();
public boolean book(int start, int end) {
for (int[] b : books)
if (Math.max(b[0], start) < Math.min(b[1], end)) return false;
books.add(new int[]{ start, end });
return true;
}
}
}
O(N^2)
O(N^2)插旗法
[10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]
- 10 1
- 20 -1
- 50 1
- 60 -1
[10, 40], [5, 15], [5, 10], [25, 55]
- 5 1
- 10 3 // [5, 15] false
- 20 -1
- 40 -1
- 50 1
- 60 -1
class MyCalendarTwo {
private TreeMap<Integer, Integer> map;
class MyCalendarTwo() {
map = new TreeMap<>();
}
public boolean book(int start, int end) {
map.put(start, map.getOrDefault(start, 0) + 1);
map.put(end, map.getOrDefault(end, 0) - 1);
int count = 0;
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
count += entry.getValue();
if (count > 3) {
// 取消这次行程
map.put(start, map.getOrDefault(start, 0) - 1);
if (map.get(start) == 0) {
map.remove(start);
}
map.put(end, map.getOrDefault(end, 0) + 1);
if (map.get(end) == 0) {
map.remove(end);
}
return false;
// ConcurrentModificationException is thrown when the iterator checks the size of the map in method "iterator.hasNext()" 因为这里直接return 了
//所以没有抛出错误
}
}
return true;
}
}
这个题,需要的最少 meeting room 的数量,和上面的题稍微有点不同。不同之处主要是如何放置当前会议的时间段,需要去找到过去已经放置好的时段里的最早结束时间,如果当前时段的开始时间晚于这个时间,就可以放到该会议时段的后面,也就是不需要新的会议室。所以需要存储以前所有的会议时段。考虑到每次需要出栈的是以前的最早结束的会议,所以采用小根堆存储,每次出栈的是按照最小的结束时间为标准。
public int minMeetingRooms(List<Interval> intervals) {
if (intervals.isEmpty() || intervals.size() == 1) return intervals.size();
Collections.sort(intervals, (a, b) -> a.start - b.start);
// 小根堆,存储会议,出堆按照结束会议的最早时间
PriorityQueue<Interval> minHeap = new PriorityQueue<>((a, b) -> a.end - b.end);
minHeap.offer(intervals.get(0));
for (int i = 1; i < intervals.size(); i++) {
Interval prev = minHeap.poll();
Interval curr = intervals.get(i);
if (curr.start >= prev.end) {
// 没有交叉,可以分配到同一个时间,将end时间延长
prev.end = Math.max(prev.end, curr.end);
} else { // 交叉,offer当前的时间间隔
minHeap.offer(curr);
}
minHeap.offer(prev);
}
return minHeap.size();
}
My Calendar III 732
A k-booking happens when k events have some non-empty intersection (i.e., there is some time that is common to all k events.)
You are given some events [start, end), after each given event, return an integer k representing the maximum k-booking between all the previous events.
Implement the MyCalendarThree class:
MyCalendarThree() Initializes the object.
int book(int start, int end) Returns an integer k representing the largest integer such that there exists a k-booking in the calendar.
Example 1:
Input
["MyCalendarThree", "book", "book", "book", "book", "book", "book"]
[[], [10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]]
Output
[null, 1, 1, 2, 3, 3, 3]
[10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]
- 10 1
- 20 -1
- 50 1
- 60 -1
[10, 40], [5, 15], [5, 10], [25, 55]
- 5 2
- 10 3 //[10, 40] -> 2 [5, 15] -> 3 [5, 10] -> 3
- 15 -1
- 20 -1
- 40 -1
- 50 1
- 60 -1
O(N^2)
Describe the Painting 1943
There is a long and thin painting that can be represented by a number line. The painting was painted with multiple overlapping segments where each segment was painted with a unique color. You are given a 2D integer array segments, where segments[i] = [starti, endi, colori] represents the half-closed segment [starti, endi) with colori as the color.
The colors in the overlapping segments of the painting were mixed when it was painted. When two or more colors mix, they form a new color that can be represented as a set of mixed colors.
For example, if colors 2, 4, and 6 are mixed, then the resulting mixed color is {2,4,6}.
For the sake of simplicity, you should only output the sum of the elements in the set rather than the full set.
You want to describe the painting with the minimum number of non-overlapping half-closed segments of these mixed colors. These segments can be represented by the 2D array painting where painting[j] = [leftj, rightj, mixj] describes a half-closed segment [leftj, rightj) with the mixed color sum of mixj.
For example, the painting created with segments = [[1,4,5],[1,7,7]] can be described by painting = [[1,4,12],[4,7,7]] because:
[1,4) is colored {5,7} (with a sum of 12) from both the first and second segments.
[4,7) is colored {7} from only the second segment.
Return the 2D array painting describing the finished painting (excluding any parts that are not painted). You may return the segments in any order.
A half-closed segment [a, b) is the section of the number line between points a and b including point a and not including point b.
Example 1:
Input: segments = [[1,4,5],[4,7,7],[1,7,9]]
Output: [[1,4,14],[4,7,16]]
Explanation: The painting can be described as follows:
- [1,4) is colored {5,9} (with a sum of 14) from the first and third segments.
- [4,7) is colored {7,9} (with a sum of 16) from the second and third segments.
Iterate on all the segments(start, end, color) of the painting and add color value at start and subtract it at end. Use TreeMap to store the points sorted. Keep adding the value of color at each point to know the current value at that point.
public List<List<Long>> splitPainting(int[][] segments) {
TreeMap<Integer, Long> map = new TreeMap<>();
for(int segment[]: segments) {
map.put(segment[0], map.getOrDefault(segment[0], 0L) + segment[2]);
map.put(segment[1], map.getOrDefault(segment[1], 0L) - segment[2]);
}
List<List<Long>> result = new ArrayList<>();
int prev = 0;
long sum = 0;
for(int key: map.keySet()) {
if(sum != 0) { // Ignore the unpainted interval
result.add(Arrays.asList((long)prev, (long)key, sum)); // Add the interval
}
sum += map.get(key);
prev = key;
}
return result;
}
Falling Squares 699
There are several squares being dropped onto the X-axis of a 2D plane.
You are given a 2D integer array positions where positions[i] = [lefti, sideLengthi] represents the ith square with a side length of sideLengthi that is dropped with its left edge aligned with X-coordinate lefti.
Each square is dropped one at a time from a height above any landed squares. It then falls downward (negative Y direction) until it either lands on the top side of another square or on the X-axis. A square brushing the left/right side of another square does not count as landing on it. Once it lands, it freezes in place and cannot be moved.
After each square is dropped, you must record the height of the current tallest stack of squares.
Return an integer array ans where ans[i] represents the height described above after dropping the ith square.
Example 1:
Input: positions = [[1,2],[2,3],[6,1]]
Output: [2,5,5]
Explanation:
After the first drop, the tallest stack is square 1 with a height of 2.
After the second drop, the tallest stack is squares 1 and 2 with a height of 5.
After the third drop, the tallest stack is still squares 1 and 2 with a height of 5.
Thus, we return an answer of [2, 5, 5].
[1,2],[2,3],[6,1]
start 1 end = 3
from = 0;
height = height = startHeight.subMap(from, end).values().stream().max(Integer::compare).get() + pos[1]; // 2
res: 2
第一轮后:[1,2] treemap:[0,0], [1,2], [3, 0]
Task Scheduler 621
Given a characters array tasks, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks could be done in any order. Each task is done in one unit of time. For each unit of time, the CPU could complete either one task or just be idle.
However, there is a non-negative integer n that represents the cooldown period between two same tasks (the same letter in the array), that is that there must be at least n units of time between any two same tasks.
Return the least number of units of times that the CPU will take to finish all the given tasks.
Example 1:
Input: tasks = ["A","A","A","B","B","B"], n = 2
Output: 8
Explanation:
A -> B -> idle -> A -> B -> idle -> A -> B
There is at least 2 units of time between any two same tasks.
- Pattern: Two Heaps,双堆类型 很多问题中,我们被告知,我们拿到一大把可以分成两队的数字。为了解决这个问题,我们感兴趣的是,怎么把数字分成两半?使得:小的数字都放在一起,大的放在另外一半。双堆模式就能高效解决此类问题。
正如名字所示,该模式用到了两个堆,是不是很难猜?一个最小堆用来找最小元素;一个最大堆,拿到最大元素。这种模式将一半的元素放在最大堆中,这样你可以从这一堆中秒找到最大元素。同理,把剩下一半丢到最小堆中,O(1)时间找到他们中的最小元素。通过这样的方式,这一大堆元素的中位数就可以从两个堆的堆顶拿到数字,从而计算出来。
判断双堆模式的秘诀:
这种模式在优先队列,计划安排问题(Scheduling)中有奇效 如果问题让你找一组数中的最大/最小/中位数 有时候,这种模式在涉及到二叉树数据结构时也特别有用 经典题目:
Find the Median of a Number Stream (medium)
Sliding Window Median (hard)
Maximize Capital (hard)
- Pattern: Subsets,子集类型,一般都是使用多重DFS 超级多的编程面试问题都会涉及到排列和组合问题。子集问题模式讲的是用BFS来处理这些问题。
这个模式是这样的:
给一组数字 [1, 5, 3]
我们从空集开始:[[]] 把第一个数(1),加到之前已经存在的集合中:[[], [1]]; 把第二个数(5),加到之前的集合中得到:[[], [1], [5], [1,5]]; 再加第三个数(3),则有:[[], [1], [5], [1,5], [3], [1,3], [5,3], [1,5,3]]. 该模式的详细步骤如下:
如果判断这种子集模式:
问题需要咱们去找数字的组合或是排列 经典题目:
Subsets (easy)
Subsets With Duplicates (easy)
Permutations (medium)
String Permutations by changing case (medium)
Balanced Parentheses (hard)
Unique Generalized Abbreviations (hard)
- Pattern: Modified Binary Search,改造过的二分 当你需要解决的问题的输入是排好序的数组,链表,或是排好序的矩阵,要求咱们寻找某些特定元素。这个时候的不二选择就是二分搜索。这种模式是一种超级牛的用二分来解决问题的方式。
对于一组满足上升排列的数集来说,这种模式的步骤是这样的:
首先,算出左右端点的中点。最简单的方式是这样的:middle = (start + end) / 2。但这种计算方式有不小的概率会出现整数越界。因此一般都推荐另外这种写法:middle = start + (end — start) / 2 如果要找的目标改好和中点所在的数值相等,我们返回中点的下标就行 如果目标不等的话:我们就有两种移动方式了 如果目标比中点在的值小(key < arr[middle]):将下一步搜索空间放到左边(end = middle - 1) 如果比中点的值大,则继续在右边搜索,丢弃左边:left = middle + 1 图示该过程的话,如下图所示:
经典题目:
Order-agnostic Binary Search (easy)
Ceiling of a Number (medium)
Next Letter (medium)
Number Range (medium)
Search in a Sorted Infinite Array (medium)
Minimum Difference Element (medium)
Bitonic Array Maximum (easy)
- Pattern: Top ‘K’ Elements,前K个系列 任何让我们求解最大/最小/最频繁的K个元素的题,都遵循这种模式。
用来记录这种前K类型的最佳数据结构就是堆了(译者注:在Java中,改了个名,叫优先队列(PriorityQueue))。这种模式借助堆来解决很多这种前K个数值的问题。
这个模式是这样的:
根据题目要求,将K个元素插入到最小堆或是最大堆。 遍历剩下的还没访问的元素,如果当前出来到的这个元素比堆顶元素大,那咱们把堆顶元素先删除,再加当前元素进去。
注意这种模式下,咱们不需要去排序数组,因为堆具有这种良好的局部有序性,这对咱们需要解决问题就够了。
识别最大K个元素模式:
如果你需要求最大/最小/最频繁的前K个元素 如果你需要通过排序去找一个特定的数 经典题目:
Top ‘K’ Numbers (easy)
Kth Smallest Number (easy)
‘K’ Closest Points to the Origin (easy)
Connect Ropes (easy)
Top ‘K’ Frequent Numbers (medium)
Frequency Sort (medium)
Kth Largest Number in a Stream (medium)
‘K’ Closest Numbers (medium)
Maximum Distinct Elements (medium)
Sum of Elements (medium)
Rearrange String (hard)
- Pattern: K-way merge,多路归并 K路归并能帮咱们解决那些涉及到多组排好序的数组的问题。
每当你的输入是K个排好序的数组,你就可以用堆来高效顺序遍历其中所有数组的所有元素。你可以将每个数组中最小的一个元素加入到最小堆中,从而得到全局最小值。当我们拿到这个全局最小值之后,再从该元素所在的数组里取出其后面紧挨着的元素,加入堆。如此往复直到处理完所有的元素。
该模式是这样的运行的:
把每个数组中的第一个元素都加入最小堆中 取出堆顶元素(全局最小),将该元素放入排好序的结果集合里面 将刚取出的元素所在的数组里面的下一个元素加入堆 重复步骤2,3,直到处理完所有数字 识别K路归并:
该问题的输入是排好序的数组,链表或是矩阵 如果问题让咱们合并多个排好序的集合,或是需要找这些集合中最小的元素 经典题目:
Merge K Sorted Lists (medium)
Kth Smallest Number in M Sorted Lists (Medium)
Kth Smallest Number in a Sorted Matrix (Hard)
Smallest Number Range (Hard)
- Pattern: 0/1 Knapsack (Dynamic Programming),0/1背包类型
https://leetcode.com/list/5ve5zu13/
经典题目:
0/1 Knapsack (medium)
Equal Subset Sum Partition (medium)
Subset Sum (medium)
Minimum Subset Sum Difference (hard)
- Pattern: Topological Sort (Graph),拓扑排序类型 拓扑排序模式用来寻找一种线性的顺序,这些元素之间具有依懒性。比如,如果事件B依赖于事件A,那A在拓扑排序顺序中排在B的前面。
这种模式定义了一种简单方式来理解拓扑排序这种技术。
这种模式是这样奏效的:
初始化 a) 借助于HashMap将图保存成邻接表形式。 b) 找到所有的起点,用HashMap来帮助记录每个节点的入度 创建图,找到每个节点的入度 a) 利用输入,把图建好,然后遍历一下图,将入度信息记录在HashMap中 找所有的起点 a) 所有入度为0的节点,都是有效的起点,而且我们讲他们都加入到一个队列中 排序 a) 对每个起点,执行以下步骤 —i) 把它加到结果的顺序中 — ii)将其在图中的孩子节点取到 — iii)将其孩子的入度减少1 — iv)如果孩子的入度变为0,则改孩子节点成为起点,将其加入队列中 b) 重复(a)过程,直到起点队列为空。
拓扑排序模式识别:
待解决的问题需要处理无环图 你需要以一种有序的秩序更新输入元素 需要处理的输入遵循某种特定的顺序 经典题目:
Topological Sort (medium)
Tasks Scheduling (medium)
Tasks Scheduling Order (medium)
All Tasks Scheduling Orders (hard)
Alien Dictionary (hard)
大家好好练练这些题目,面试中遇到中高等难度的题目,应该就能解得不错了。
第二门则是单独将动态规划(DP)的题目进行了细分。
提到算法,绕不开的重点和难点就肯定会包括动态规划 – DP,本文就把经典的DP问题按照分类列一下,大家可以按照Recursion,Top-Down,Bottom-Up三种方式都练一练。俗话说,熟能生巧,多练才是提高算法的不二法宝。
课程详细的内容,可以参考这里:
Grokking Dynamic Programming Patterns for Coding Interviews www.educative.io/courses/grokking-dynamic-programming-patterns-for-coding-interviews?aff=K7qB
该门课程中, 作者将DP的问题分成以下几类:
- 0/1 Knapsack, 0/1背包,6个题 0/1 Knapsack,0/1背包问题
Equal Subset Sum Partition,相等子集划分问题
Subset Sum,子集和问题
Minimum Subset Sum Difference,子集和的最小差问题
Count of Subset Sum,相等子集和的个数问题
Target Sum,寻找目标和的问题
- Unbounded Knapsack,无限背包,5个题 Unbounded Knapsack,无限背包
Rod Cutting,切钢条问题
Coin Change,换硬币问题
Minimum Coin Change,凑齐每个数需要的最少硬币问题
Maximum Ribbon Cut,丝带的最大值切法
- Fibonacci Numbers,斐波那契数列,6个题 Fibonacci numbers,斐波那契数列问题
Staircase,爬楼梯问题
Number factors,分解因子问题
Minimum jumps to reach the end,蛙跳最小步数问题
Minimum jumps with fee,蛙跳带有代价的问题
House thief,偷房子问题
- Palindromic Subsequence,回文子系列,5个题 Longest Palindromic Subsequence,最长回文子序列
Longest Palindromic Substring,最长回文子字符串
Count of Palindromic Substrings,最长子字符串的个数问题
Minimum Deletions in a String to make it a Palindrome,怎么删掉最少字符构成回文
Palindromic Partitioning,怎么分配字符,形成回文
- Longest Common Substring,最长子字符串系列,13个题 Longest Common Substring,最长相同子串
Longest Common Subsequence,最长相同子序列
Minimum Deletions & Insertions to Transform a String into another,字符串变换
Longest Increasing Subsequence,最长上升子序列
Maximum Sum Increasing Subsequence,最长上升子序列和
Shortest Common Super-sequence,最短超级子序列
Minimum Deletions to Make a Sequence Sorted,最少删除变换出子序列
Longest Repeating Subsequence,最长重复子序列
Subsequence Pattern Matching,子序列匹配
Longest Bitonic Subsequence,最长字节子序列
Longest Alternating Subsequence,最长交差变换子序列
Edit Distance,编辑距离
Strings Interleaving,交织字符串
😼😼😼😼
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/MPnaiL
感谢:她们都有题库在 github 上直接有,都是大神, 都富有详细解法,对新人特别适合了解解题思路