Description
Example
Solution 1(C++)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
ListNode dummy(INT_MIN);
ListNode *tail = &dummy;
while (l1 && l2) {
if (l1->val < l2->val) {
tail->next = l1;
l1 = l1->next;
} else {
tail->next = l2;
l2 = l2->next;
}
tail = tail->next;
}
tail->next = l1 ? l1 : l2;
return dummy.next;
}
};
Solution 2(C++)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* a, ListNode* b) {
if (!a || b && a->val > b->val) swap(a, b);
if (a) a->next = mergeTwoLists(a->next, b);
return a;
}
};
算法分析
解法一: 解法一就是我最开始的思路,但是呢,我没写出来,考虑得太多太杂,别人的代码非常的简洁有效。值得好好学习。在我写这道题的过程中,第一次错误的地方就是算法设计出错了。
我最开始是这样合并的:cur1与cur2分别是l1与l2的两个当前结点,如果cur1大于cur2(结点的值进行比较),那么就将cur1接在cur2的后面,然后让cur1更新为原来cur1的下一个结点,下一个结点再与cur2比较,谁小谁就接在上一个结点的后面。但是这种方法有个问题,如果两个链表:[1,2,4]与[1,3,4]按照我这样做就会变成[1,1,3,2,4,4]。
所以问题出在哪呢?出在这种方法一开始是会一次性排序两个结点,然后更新的是这两个结点中较小的那个结点的下一个结点,但是不能保证这下一个结点就一定比之前一次比较中较大的结点的下一个结点小。比如例子中的2与3。最开始两个1相比,虽然一样,但设定cur2的1小于cur1的1,所以cur1的1排在cur2的后面,那么这时候按照之前的算法,我会去比较cur2的1的下一个结点3与cur1的1,结果自然3应该在1的后面,而这时cur1的2比较就只能在3的后面呢。
所以应该怎么改?每次只排序一个。详细来说就是,cur1与cur2相比,小的一个作为确定的新结点。这就只确定了一个。下一个怎么确定呢?让小的结点更新为下一个结点,与大的结点相比,其结果作为第一次确定的结点的下一个结点。然后如此循环,就是每次只确定一个结点。这就是解法一的思路。
解法二: 至于解法二,只能说目前的我还是佩服,居然能想到用递归的方法来做,而且就利用题目事先给定的函数。思路就是如果我们现在要融合l1与l2两个链表,假设l1当前结点小于l2,那么,l1当前结点的下一个结点不就是l1除去当前结点之后的链表与l2链表再融合的结果么。所以就这样,算法第一行就是确保a结点永远小于b结点。第二行当a链表不为空时,就可以递归完成。如果a链表为空,那么也会在第一行对a与b进行对换。妙。
在后面的学习中,也用到了类似的迭代函数方法来解决问题,可参考:LeetCode-326. Power of Three、 LeetCode-263. Ugly Number、 LeetCode-784. Letter Case Permutation。
程序分析
解法一中可以掌握根据现有链表创建一个新结点的方法:
ListNode dummy(INT_MIN);
ListNode *tail=&dummy;
ListNode *dummy= new ListNode(INT_MIN);
ListNode *tail=dummy;
//return要改为:
return dummy->next;
然后链表的题,往往可以通过设定一个不存在的、假象的一前一后的哨兵来帮助编程,比如这里的tail,又比如: LeetCode-206. Reverse Linked List 里解法二的new_head。所以学习这种思想,或许是做不出题目的时候的切入点。
然后链表虽然是一种数据结构,但是都是以链表的最开头的结点来代表这个链表的。所以l1与l2,在编程解题的时候就是天然系统设好的变量,充分利用它们可以避免重复的设置新的变量。
对于解法二,我觉得一方面要多观察题目的特点,还有一方面就是要多多做题,多多积累了。说到积累,目前我做了62道题目,虽然都是Easy难度的题,但是里面不少题目还是有许多可以重新学习的地方,那么花了这么大精力一道题一道题的记录,就需要多回头来看看。