jadvpetc

导航

[Leetcode] 21 & 23 & 56 & 57: Intervals

Merge Two Sorted Lists

Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.

Example:

Input: 1->2->4, 1->3->4
Output: 1->1->2->3->4->4

题目理解:把两个排好序的linkedlist merge起来,然后返回merge好的linkedlist的头节点。
关于linkedlist的题目一般都要准备好双指针,然后通过双指针的“速度”“方向”“遍历”来完成.这道题也是需要使用双指针:
题目给了一个很关键的提示:两个linkedlist都是排好序的,那么我们可以得到以下的结论:
1. let s1 = head of list 1, s2 = head of list 2
2. if s1 < s2 and s1 -> next < s2, then there does not exist any element e such that s1 < e < s2.
因为我们只有两个linkedlist,所以这个结论我们可以很容易通过contradiction来证明。
在实施起来也非常容易. 这里需要使用一个dummy 和curr节点分别在每次遍历的时候更新和用作返回值。

解法1:
 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode* mergeTwoLists(ListNode* l1, ListNode* l2)
12     {
13         ListNode *dummy = new ListNode(-1), *curr = dummy;
14         while (l1 && l2) {
15             if (l1->val < l2->val) {
16                 dummy->next = l1;
17                 l1 = l1->next;
18             } else { // if l1 and l2 are equal, then choose l2 (you can also choose l1)
19                 dummy->next = l2;
20                 l2 = l2->next;
21             }
22             dummy = dummy->next;
23         }
24         if (l1)
25             dummy->next = l1;
26         else if (l2)
27             dummy->next = l2;
28         return curr->next;
29     }
30 };


Merge K sorted Lists

 

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.

Example:

Input:
[
  1->4->5,
  1->3->4,
  2->6
]
Output: 1->1->2->3->4->4->5->6
题目理解:与上一道题不同的是这次是k个linkedlist,所以最简单能想到的方法便是继续套用两个的做法:

1. merge list i with j, where j != i and j is in the range of k - i
2. when finish list i, merge i + 1 until i == k

这个做法其实就是把每两个都merge一遍,这样的复杂度便是:
1. assume the average length of each list is m
2. to merge i = 1 with all the valid j: it will require (k + k) + (2k + k) + (3k + k) ... + ((m - 1)k + k) = 2k + 3k + 4k + ... + mk = O(m2 * k)

这样做的复杂度会比较高,我们可以通过一些别的方法来降低复杂度.这个算法实际上并没有充分利用排好序的特征,只是在merge的时候保证是可以通过线性扫描来完成。
一般对于排好序的数组一般还是应该先想到二分法和快速/归并排序。首先最直观的方法便是使用一个priority queue来实现:假如不是一个linkedlist,那么我们实际上
只需要每次都把所有的数字push进我们的heap其实就可以了,这样时间复杂度便是:
1. first list will require m insertions, for each insertion the complexity will be log m (worst case), so total will be m log m
2. second list require 2m log 2m
3. total will be m log m + 2m log 2m + 3m log 3m + ... + km log km = O(m * k2)
这里其实可以看到这个算法并没有提高太多,如果我们的k远大于averge length m,那么实际上这个算法并没有提升多少。假如我们不用每次都把所有之前的数字都遍历一遍,或者通过某种方式降低每次

push进去的node的数量其实就可以有效的降低我们的复杂度。那么就可以每次都放k个节点进到我们的priority queue中,我们每次保证这个queue只有k个元素,这样时间复杂度就会大量的降低:
1. push all the first nodes into priority queue (pq).
2. pop the first node by from pq, so that we get the smallest node among the k head nodes
3. append the pop node to our return list, and push the next node from the node we just pop
4. going back to 2 until there is no more nodes left from pq
这个方法可以保证每次push进去的复杂度永远是log k,一共有m * k 个节点,所以总的时间复杂度是O(m * k * log k),这也是这道题的最终结果:

解法1:
 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode* mergeKLists(vector<ListNode*>& lists) {
12         auto cmp = [](const ListNode* left, const ListNode* right) {
13             return (left -> val) > (right -> val);
14         };
15         priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> pq(cmp);
16         ListNode* dummy = new ListNode(-1);
17         ListNode* curr = dummy;
18         for (auto node : lists)
19             if (node)
20                 pq.push(node);
21         
22         while (!pq.empty()) {
23             auto t = pq.top();
24             pq.pop();
25             curr -> next = t;
26             curr = curr -> next;
27             if (t && t -> next)
28                 pq.push(t -> next);
29         }
30         return dummy -> next;
31     }
32 };

 

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.

题目理解:给一个没有顺序的interval数组,现在要把所有有交错的interval都merge一起,返回最终的结果。
第一步是先按照每一个interval的起始位置排序,然后遍历一遍把所有 current end <= next start 的两个interal都merge一次,这样最后的list就是已经merge好的了

解法1:
 1 #define vvi vector<vector<int>>
 2 #define vi vector<int>
 3 #define pb push_back
 4 class Solution {
 5 public:
 6     vi mergeTwo(vi& l, vi& r) {
 7         return {min(l[0], r[0]), max(l[1], r[1])};
 8     }
 9     vvi merge(vvi& in) {
10         vvi res;
11         sort(in.begin(), in.end());
12         for (int i = 0; i < in.size(); ++i) {
13             if (res.empty() || res.back()[1] < in[i][0])
14                 res.pb(in[i]);
15             else
16             {
17                 vi merged = mergeTwo(res.back(), in[i]);
18                 res.pop_back();
19                 res.pb(merged);
20             }
21                 
22         }
23         return res;
24     }
25 };


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.


Example 1:


Input: intervals = [[1,3],[6,9]], newInterval = [2,5]
Output: [[1,5],[6,9]]

Example 2:


Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]
Output: [[1,2],[3,10],[12,16]]
Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].

题目理解:给一个数组,每个element都是[start, end],代表一个interval。现在要插入一个新的interval:[new start, new end],返回最终数组。
如果新的interval与之前的任意的interval有交错的话:new start < start or new end > end,则需要把这种的interval merge一起。我们可以先看几个test:1. [[1, 3], [10, 13]] - insert [5, 7] -> [[1, 3], [5, 7], [10, 13]] (no intersection)2. [[1, 3], [5, 10]] - insert [3, 5] -> [[1, 3], [3, 5], [5, 10]] -> [[1, 5], [5, 10]] -> [[1, 10]] (multiple intersections)如果只有两个interval的话,这个问题便很好解决:1. old interval list = [[start, end]]2. insert new interval [new start, new end]3. new interal list = [[min(start, new start), max(new end, end)]]实际上我们需要merge的只有交错的interval,所以如果能把所有会交错的数组通过某种方式都拿出来,那么我们只要把所有的这些交错的数组种取最小的start和最大的end便可以组成我们新的数组:1. create a list that holds all the intervals that have end < new start; create another list that holds all the intervals that have new end < start2. so the rest of the intervals are all going to intersect with new interval, we keep updating our new start and new end once we get a new one that will intersect with new interval.3. concatenate the two list, and insert the new interval at the middle. (O(n))

这样的算法时间复杂度是O( n) 因为我们在遍历前首先要排序,之后要再次遍历整个数组。

解法1:

 1 class Solution {
 2     public:
 3         vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
 4             vector<vector<int>> left, right;
 5             int s = newInterval[0], e = newInterval[1];
 6             for (auto& i : intervals) {
 7                 if (i[1] < s) { // all less than newInterval
 8                     left.push_back(i);
 9                 } else if (i[0] > e) { // all greater than newInterval
10                     right.push_back(i);
11                 } else {
12                     s = min(s, i[0]); // the one needs to be merged
13                     e = max(e, i[1]);
14                 }
15             }
16             left.push_back({s, e});
17             // O(n)
18             left.insert(left.end(), right.begin(), right.end());
19             return left;
20         }
21 };

 

 

posted on 2019-11-25 10:24  jadvpetc  阅读(160)  评论(0编辑  收藏  举报