題目
將兩個升序鏈表合並為一個新的 升序 鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。
示例:
輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4
分析與題解
題解一(遞歸)
在忽略空鏈表的情況下,利用遞歸的思想依次對兩個鏈表的元素進行比較和合並,大致思路如下:
也就是說,兩個鏈表頭部值較小的一個節點與剩下元素的 merge
操作結果合並。如果 l1
或者 l2
一開始就是空鏈表 ,那么沒有任何操作需要合並,所以我們只需要返回非空鏈表。否則,我們要判斷 l1
和 l2
哪一個鏈表的頭節點的值更小,然后遞歸地決定下一個添加到結果里的節點。如果兩個鏈表有一個為空,遞歸結束。
代碼如下:
/**
* 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* l1, ListNode* l2) {
//如果某個指針為空指針,直接返回飛空指針即可
if(l1 == nullptr)
return l2;
else if(l2 == nullptr)
return l1;
else if(l1->val < l2->val){
l1->next = mergeTwoLists(l1->next,l2);
return l1;
}
else{
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
}
};
題解二(迭代)
我們可以用迭代的方法來實現上述算法。當l1
和l2
都不是空鏈表時,判斷l1
和l2
哪一個鏈表的頭節點的值更小,將較小值的節點添加到結果里,當一個節點被添加到結果里之后,將對應鏈表中的節點向后移一位。
首先,我們設定一個哨兵節點 prehead
,這可以在最后讓我們比較容易地返回合並后的鏈表。我們維護一個 prev 指針,我們需要做的是調整它的 next 指針。然后,我們重復以下過程,直到 l1
或者 l2
指向了 null :如果 l1
當前節點的值小於等於 l2
,我們就把 l1
當前的節點接在prev
節點的后面同時將 l1
指針往后移一位。否則,我們對 l2
做同樣的操作。最后不管將哪一個元素接在了后面,我們都需要把 prev 向后移一位。
在循環終止的時候, l1
和 l2
至多有一個是非空的。由於輸入的兩個鏈表都是有序的,所以不管哪個鏈表非空,它包含的所有元素都比前面已經合並鏈表中的所有元素都要大。這意味着我們只需要簡單地將非空鏈表接在合並鏈表的后面,並返回合並鏈表即可。
注意在while循環終止時,非空鏈表的剩余元素還沒有與prev
連接起來。所以退出循環后還需要再在指定下next為非空剩余鏈表的頭指針。代碼如下:
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* preHead = new ListNode(-1);
ListNode* prev = preHead;
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
prev->next = l1;
l1 = l1->next;
} else {
prev->next = l2;
l2 = l2->next;
}
prev = prev->next;
}
// 合並后 l1 和 l2 最多只有一個還未被合並完,我們直接將鏈表末尾指向未合並完的鏈表即可
prev->next = l1 == nullptr ? l2 : l1;
return preHead->next;
}
};