题目:
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4
方法1.拆分为合并两个有序链表
思路:
思路方面就是拆分为两个有序链表,合并时这里我选用的是双指针的方法,如果你有更好的方法当然都可以用,下面给大家讲解一下我的代码
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
if(list1 == nullptr){
return list2;
}
if(list2 == nullptr){
return list1;
}
ListNode* HEAD = new ListNode(0);
ListNode* index = HEAD;
ListNode* p1 = list1;
ListNode* p2 = list2;
while(p1 != nullptr && p2 != nullptr){
if(p1->val > p2->val){
index->next = p2;
p2 = p2->next;
}
else{
index->next = p1;
p1 = p1->next;
}
index = index->next;
}
if(p1 != nullptr){
index->next = p1;
}
if(p2 != nullptr){
index->next = p2;
}
return HEAD->next;
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size() == 0){
return nullptr;
}
int k = lists.size();
ListNode* p = nullptr;
for(int i = 0; i < k; i++){
p = mergeTwoLists(p, lists[i]);
}
return p;
}
};
讲解:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size() == 0){
return nullptr;
}
int k = lists.size();
ListNode* p = nullptr;
for(int i = 0; i < k; i++){
p = mergeTwoLists(p, lists[i]);
}
return p;
}
首先这段代码就是这道题目一进来的地方,首先判断vector数组长度是否为0,其次开始将数组进行合并,下面来着重讲解一下如何合并
if(list1 == nullptr){
return list2;
}
if(list2 == nullptr){
return list1;
}
ListNode* HEAD = new ListNode(0);
ListNode* index = HEAD;
ListNode* p1 = list1;
ListNode* p2 = list2;
上来两个if判断来防止出现空指针的出现,其次设计一个需头节点(常规操作),index代表我们需要向哪里合并,p1和p2代表list1和list2
while(p1 != nullptr && p2 != nullptr){
if(p1->val > p2->val){
index->next = p2;
p2 = p2->next;
}
else{
index->next = p1;
p1 = p1->next;
}
index = index->next;
}
后面进入while循环,判断条件是p1和p2指向的节点不能有一个为空(就是至少一条节点遍历结束),进入while,第一个if就是若p1指向的节点的值更大的话,我们将index的next指针指向p2指向的节点 ,然后让p2指针指向下一个节点,else的话同理,同理index也要向后移动。
if(p1 != nullptr){
index->next = p1;
}
if(p2 != nullptr){
index->next = p2;
}
return HEAD->next;
当代码运行到这里就代表至少一条列表已经遍历结束,换句话说就是若有一条链表没有遍历结束,那么剩下的节点都是更大的节点,所以我们只要将剩下的链表直接接到index后面即可,最后返回需头节点的下一个节点就完成了。
方法2.优先队列
思路:
这道题我们使用优先级队列,但是我们需要自己维护一个比较器来完成,具体的我将在下面的代码讲解里面说明
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
struct comp{
bool operator()(ListNode* a, ListNode* b){
return a->val > b->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size() == 0){
return nullptr;
}
ListNode* HEAD = new ListNode(0);
ListNode* p = HEAD;
priority_queue<ListNode*, vector<ListNode*>, comp> pq;
for(auto it : lists){
if(it != nullptr){
pq.push(it);
}
}
while(!pq.empty()){
ListNode* temp = pq.top();
pq.pop();
p->next = temp;
if(temp->next != nullptr){
pq.push(temp->next);
}
p = p->next;
}
return HEAD->next;
}
};
讲解:
struct comp{
bool operator()(ListNode* a, ListNode* b){
return a->val > b->val;
}
};
这就是我们写的比较器
if(lists.size() == 0){
return nullptr;
}
ListNode* HEAD = new ListNode(0);
ListNode* p = HEAD;
priority_queue<ListNode*, vector<ListNode*>, comp> pq;
首先判断lists的长度是否为0,然后设置一个虚头节点,p指针指向虚头节点,然后设置一个优先级队列pq
for(auto it : lists){
if(it != nullptr){
pq.push(it);
}
}
将各个链表的头节点加入优先级队列
while(!pq.empty()){
ListNode* temp = pq.top();
pq.pop();
p->next = temp;
if(temp->next != nullptr){
pq.push(temp->next);
}
p = p->next;
}
return HEAD->next;
进入while循环,判断条件为优先级队列不为空,首先返回优先级队列首个元素(也就是最小的那个节点,因为我们维护的是最小堆)的引用,然后再将其弹出,p的next指针指向刚才取出的节点,然后去判断刚才取出的节点是否有下一个节点,若有,将其加入到优先级队列,这样就保证了我们这个优先级队列中最少有K个元素(K为链表的条数),然后p指针指向下一个节点。最后返回需头节点的下一个节点,结束。