面试题25. 合并两个排序的链表 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
示例1: 输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4
限制:0 <= 链表长度 <= 1000
双百解法一:迭代法 修改原有链表的指向 推荐!!!!
设l1为 1->3->5->7; l2为 2->4->6->8
1.首先分析合并两个链表的过程。
我们的分析从合并两个链表的头节点开始。链表1的头节点的值小于链表2的头节点的值,因此链表1的头节点将是合并后链表的头节点.
我们继续合并两个链表中剩余的节点。在两个链表中剩下的节点依然是排序的,因此合并这两个链表的步骤和前面的步骤是一样的。
我们继续比较剩下的链表结构中现有两个头节点的值。此时链表2的头节点的值小于链表1的头节点的值,因此链表2的头节点的值将是合并剩余节点得到的链表的头节点。
我们把这个节点和前面合并链表时得到的链表的尾节点(即链表1中最初的值为1的头节点)链接起来。
当我们得到两个链表中值较小的头节点并把它链接到已经合并的链表之后,两个链表剩余的节点依然是排序的,因此合并的步骤和之前的步骤是一样的。
这就是典型的递归过程,我们可以定义递归函数完成这一合并过程。
2.接下来我们来解决鲁棒性的问题。
每当代码试图访问空指针指向的内存时程序就会崩溃,从而导致鲁棒性问题。
我们要对空链表单独处理。当第一个链表是空链表,那么把它和第二个链表合并,显然合并的结果就是第二个链表。
同样,当输入的第二个链表为空链表的时候,它和第一个链表合并得到的结果就是第一个链表。
如果两个链表都是空链表,则合并的结果是得到一个空链表
则有Java实现的代码如下:
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1;
if(l1.val<=l2.val) {
l1.next = mergeTwoLists(l1.next,l2); //说明此时从l1中取头结点
return l1;
}
else {
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
双百解法二:仿归并排序的思路+伪头节点 推荐!!!
其中归并排序见:https://blog.csdn.net/qq_30476717/article/details/105254580
引入伪头节点: 由于初始状态合并链表中无节点,因此循环第一轮时无法将节点添加到合并链表中。
解决方案:初始化一个辅助节点 dum作为合并链表的伪头节点,将各节点添加至 dum之后。
时间复杂度 O(M+N): M, N分别为链表 l1,l2的长度,合并操作需遍历两链表。
空间复杂度 O(1): 节点引用 dum,cur使用常数大小的额外空间。
public ListNode mergeTwoLists2(ListNode l1, ListNode l2) {
ListNode dum = new ListNode(0);
ListNode cur = dum;
while(l1!=null&&l2!=null) {
if(l1.val<=l2.val) {
cur.next = l1;
l1 = l1.next;
}else {
cur.next = l2;
l2= l2.next;
}
cur = cur.next;
}
cur.next=l1!=null?l1:l2;
//若是上面循环后l1或l2中还有剩余,则直接合并入结果链表中即可
//如果初始l1,l2中存在空链表,则不会进入上面的循环,直接进入这条语句进行判断 故可以省去最开始的两句if
return dum.next;
}
双百解法三:归并排序的思路,新建一个新的链表,不改变原有链表结构 不是很推荐
public ListNode mergeTwoLists3(ListNode l1, ListNode l2) {
if(l1==null) return l2;//这里的两句if不能省
if(l2==null) return l1;
ListNode tmp=new ListNode(0);
ListNode res = tmp; //指向合并链表的头结点,便于返回
//ListNode add = new ListNode(0);
while(l1!=null&&l2!=null){
if(l1.val<=l2.val) {
tmp.val = l1.val;
l1=l1.next;
}
else {
tmp.val = l2.val;
l2=l2.next;
}
if(l1!=null&&l2!=null){
tmp.next = new ListNode(0);
//这里不能通过在前面初始化一个add,ListNode add = new ListNode(0); 然后tmp.next = add;来实现
//这里需要不断地建立一个新的节点,而非指向一个固定的节点
tmp = tmp.next;
}
}
if(l1!=null){
tmp.next = l1;
}
if(l2!=null){
tmp.next = l2;
}
return res;
}