
cpp
/**
* 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) {
//迭代写法
ListNode* dummy = new ListNode(0);
ListNode* head = dummy;
if(list1 == nullptr) return list2;
if(list2 == nullptr) return list1;
while(list1 != nullptr && list2 != nullptr){
if(list1->val > list2->val){
dummy->next = list2;
list2 = list2->next;
}else{
dummy->next = list1;
list1 = list1->next;
}
dummy = dummy->next;
}
if(list1 == nullptr){
dummy->next = list2;
}else{
dummy->next = list1;
}
return head->next;
}
};
今天复习的是合并链表系列,合并2个链表简单题不再赘述。

有一个非常唐的做法就是依次两两合并K个链表。
cpp
/**
* 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* mergeKLists(vector<ListNode*>& lists) {
int len = lists.size();
if(len == 0) return nullptr;
if(len == 1) return lists[0];
ListNode* curr = new ListNode(0);
curr = lists[0];
for(int i = 1; i<len;i++){
curr = mergeTwoLists(curr,lists[i]);
}
return curr;
}
ListNode* mergeTwoLists(ListNode* l1,ListNode* l2) {
if(l1 == nullptr) return l2;
if(l2 == nullptr) return l1;
if(l1->val <= l2->val){
l1->next = mergeTwoLists(l1->next,l2);
return l1;
}
else{
l2->next = mergeTwoLists(l1,l2->next);
return l2;
}
}
};
然而我们可以用分治来优化上面这种解法:
cpp
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
return MergeKLists(lists, 0, lists.size()-1);
}
ListNode* MergeKLists(vector<ListNode*>& lists, int l, int r)
{
if (l > r) return nullptr;
if (l == r) return lists[l];
int mid = l + (r - l) / 2;
auto left = MergeKLists(lists, l, mid);
auto right = MergeKLists(lists, mid+1, r);
return MergeTwoLists(left, right);
}
ListNode* MergeTwoLists(ListNode* l1, ListNode* l2)
{
ListNode* dummy = new ListNode();
ListNode* cur = dummy;
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
cur->next = l1;
l1 = l1->next;
} else {
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
cur->next = l1 != nullptr ? l1 : l2;
return dummy->next;
}
};
这个是把O(n)优化成了O(log2n),一个一个合并是顺序遍历,分治来做的话让他自己内部合并,而不用像遍历一样去维护一个ans。还有一个方法是用最小堆来做,也是容易想到的,所以这道题目应该不算是困难题。

cpp
/**
* 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* sortList(ListNode* head) {
if(head == nullptr || head->next == nullptr){
return head;
}
ListNode* mid = findMiddle(head);
ListNode* rightHead = mid->next;
mid->next = nullptr;
ListNode* l1 = sortList(head);
ListNode* l2 = sortList(rightHead);
return mergeTwoLists2(l1,l2);
}
ListNode* findMiddle(ListNode* head) {
ListNode* slow = head;
ListNode* fast = head->next;
while(fast != nullptr && fast->next != nullptr){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
//合并有序链表1
ListNode* mergeTwoLists(ListNode* l1,ListNode* l2){
ListNode* dummy = new ListNode(0);
ListNode* curr = dummy;
while(l1 != nullptr && l2 != nullptr){
if(l1->val > l2->val){
curr->next = l2;
l2 = l2->next;
}else if(l1->val <= l2->val){
curr->next = l1;
l1 = l1->next;
}
curr = curr->next;
}
if(l1 == nullptr){
curr->next = l2;
}else{
curr->next = l1;
}
return dummy->next;
}
//合并有序链表2
ListNode* mergeTwoLists2(ListNode* l1,ListNode* l2){
if(l1 == nullptr){
return l2;
}if(l2 == nullptr){
return l1;
}
if(l1->val > l2->val) {
l2->next = mergeTwoLists2(l1,l2->next);
return l2;
}else{
l1->next = mergeTwoLists2(l1->next,l2);
return l1;
}
}
};
这实际上也是分治(归并排序)。归并给我的感觉就像是你很难处理一个棘手的问题,然后你把这个棘手的问题一步步拆解下放,直到你能解决的地步,比如这里对于链表排序就是拆解到只剩下两个节点的时候,你肯定能通过判断大小关系直接排序了,然后一步步回升,直到解决这个问题。
这里有个工具函数,找链表中点我们可以记忆一下。这是一个快慢指针,这个判断条件你可以画图证明一下,如果节点总数是偶数,最后fast->next == nullptr。如果节点总数是奇数,最后fast == nullptr。都是比较好证明的。
OK,今天看的这几道题都不是很难,后面两道用到了归并的思想。