题目:题目链接
就合并链表?这是hard嘛?就挺离谱的。我都不敢相信。。。
有k个链表,每次将一个链表和新链表合并就可以了。
例如样例:[[1,4,5],[1,3,4],[2,6]],设新链表为l,初始为空链表
首先l与[1,4,5]合并,l = 1,4,5
然后l与[1,3,4]合并,l = 1,1,3,4,4,5
以此类推。
基本上和归并排序一样的,只不过归并排序合并的是数组,这是链表。合并两个链表都会吧,不会最好自己动手模拟一下,就是双指针的用法。
看代码吧:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { //同归并排序
if (!(l1 && l2)) return l1 ? l1 : l2; //有一个为空就不用排序了
ListNode* root = new ListNode(); //创建一个头结点,用来连接排好序的链表
ListNode* dummy_node = root; //记得要保存下root,因为后面root会移动
ListNode* ptr1 = l1; //指向l1的第0个结点
ListNode* ptr2 = l2; //指向l2的第0个结点
while (ptr1 && ptr2) { //有一个结点没了,就不比较了
if (ptr1->val > ptr2->val) { //如果ptr2->val更小,那就让root的下一个结点是ptr2。反之亦然
root->next = ptr2;
ptr2 = ptr2->next; //记得要向后移
}
else { //同if
root->next = ptr1;
ptr1 = ptr1->next;
}
root = root->next; //root要始终指向新链表的尾部
}
root->next = ptr1 ? ptr1 : ptr2; //如果有一个链表还没合并到新链表去,直接让他们合并
return dummy_node->next; //注意,返回的是头结点的下一个。因为此时root已经指向新链表的尾部了
}
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len = lists.size();
if (len == 0) return {}; //没有链表
ListNode* root = nullptr; //root是新链表的头指针,为了让lists[0]和其他链表一样,都有前驱结点
for (int i = 0; i < len; ++i) //每次将一个链表合并到新链表中
root = mergeTwoLists(root, lists[i]);//root始终为新链表的头指针
return root;
}
};
这个其实挺慢的,200多ms,有没有更简单的方法?有。
我们可以用优先队列,把所有链表的当前最小值都放进去,然后每次挑最小的就可以了。看代码:
class Solution {
public:
struct cmp {//优写队列从小到大排序
bool operator()(ListNode* a, ListNode* b) {
return a->val > b->val;
}
};
priority_queue<ListNode*, vector<ListNode*>, cmp> q;
ListNode* mergeKLists(vector<ListNode*>& lists) {
int len = lists.size();
if (len == 0) return {};
// priority_queue<ListNode*,vector<ListNode*>,cmp>q;
ListNode* root = new ListNode();
ListNode* dummy_node = root;
for (auto& list : lists) {//先将所有链表的第一个都加入到优先队列中去
if (list)//空的就算了
q.push(list);
}
while (!q.empty()) {//每次把最小的弹出,接到root后面
ListNode* min_node = q.top();
q.pop();
root->next = min_node;
root = root->next;
if (min_node->next)//出去后,把它的后面那个叫进来
q.push(min_node->next);
}
return dummy_node->next;
}
};
这样速度特别快!
说实在话,这题真不应该是hard。。。。。
加油加油加油!