题目:输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。例如输入图中的链表1和链表2,则合并之后的升序链表3所示。
这是一个经常被各公司采用的面试题。在面试过程中,我们发现应聘者最容易犯两个错误:一是在写代码之前没有对合并的过程想清楚,最终合并出来的链表要么中间断开要么并没有递增排序;二是代码的鲁棒性方面存在问题,程序一旦有特殊的输入(如空链表)就会崩溃。接下来分析如何解决这个问题。
首先分析合并两个链表的过程。我们的分析从合并两个链表的头结点开始。链表1的头结点的值小于链表2的头结点的值,因此链表1的头结点将是合并后链表的头结点,如图所示:
我们继续合并两个链表中剩余的结点。在两个链表中剩下的结点依然是排序的,因此合并这两个链表的步骤和前面的步骤是一样的。我们还是比较两个头结点的值。此时链表2的头结点的值小于链表1的头结点的值,因此链表2的头结点的值将是合并剩余结点得到的链表的头结点。我们把这个结点和前面合并链表时得到的链表的尾节点链接起来,如图所示。
当我们得到两个链表中值较小的头结点并把它链接到已经合并的链表之后,两个链表剩余的结点依然是排序的,因此合并的步骤和之前的步骤是一样的。这就是典型的递归的过程,我们可疑定义递归函数完成这一合并过程。
接下来我们来解决鲁棒性的问题。每当代码试图访问空指针指向的内存时程序就会崩溃,从而导致鲁棒性问题。在本题中一旦输入空的链表就会引入空的指针,因此我们要对空链表单独处理。当第一个链表是空链表,也就是它的头结点是一个空指针时,那么把它和第二个链表合并,闲人合并的过程就是第二个链表。同样,当输入的第二个链表的头结点是空指针的时候,我们把它和第一个链表合并得到的结果就是第一个链表。如果两个链表都是空链表,合并的结果是得到一个空链表。
代码:
public class Offer17 {
public static class ListNode{
int value;
ListNode next = null;
ListNode(int val){
this.value = val;
}
}
//递归解决
public ListNode mergeRecursive(ListNode head1, ListNode head2){
if(head1 == null)
return head2;
else if(head2 == null)
return head1;
ListNode pMergedHead = null;
if(head1.value < head2.value){
pMergedHead = head1;
pMergedHead.next = mergeRecursive(head1.next, head2);
}else{
pMergedHead = head2;
pMergedHead.next = mergeRecursive(head1, head2.next);
}
return pMergedHead;
}
//非递归,采用不带头节点的链表
public ListNode merge(ListNode head1, ListNode head2){
if(head1 == null )
return head2;
if(head2 == null)
return head1;
ListNode newHead = null;
ListNode tempHead= null;
while(head1 != null && head2 != null){
if(head1.value <head2.value){
if(newHead == null){
newHead = head1;
tempHead = head1;
}else{
tempHead.next = head1;
tempHead = tempHead.next;
}
head1 = head1.next;
}else{
if(newHead == null){
newHead = head2;
tempHead = head2;
}else{
tempHead.next = head2;
tempHead = tempHead.next;
}
head2 = head2.next;
}
}
if(head1 == null)
tempHead.next = head2;//第一次把tempHead.next写成tempHead,出错了
else
tempHead.next = head1;
return newHead;
}
public static void print(ListNode head){
if(head == null)
System.out.println("List is null");
while(head != null){
System.out.print(head.value+", ");
head = head.next;
}
System.out.println();
}
public static void main(String[] args) {
ListNode head1 = new ListNode(1);
ListNode node2 = new ListNode(3);
ListNode node3 = new ListNode(5);
ListNode node4 = new ListNode(7);
head1.next = node2;
node2.next = node3;
node3.next = node4;
ListNode head2 = new ListNode(2);
ListNode node5 = new ListNode(4);
ListNode node6 = new ListNode(6);
ListNode node7 = new ListNode(8);
head2.next = node5;
node5.next = node6;
node6.next = node7;
ListNode head3 = new ListNode(2);
ListNode node8 = new ListNode(4);
ListNode node9 = new ListNode(6);
ListNode node10 = new ListNode(8);
head3.next = node8;
node8.next = node9;
node9.next = node10;
ListNode head4 = new ListNode(2);
ListNode node11 = new ListNode(4);
ListNode node12 = new ListNode(6);
ListNode node13 = new ListNode(8);
head4.next = node11;
node11.next = node12;
node12.next = node13;
Offer17 of17 = new Offer17();
//功能测试(输入的两个链表有多个节点,节点的值互不相同
//print(of17.mergeRecursive(head1, head2));
//print(of17.merge(head1, head2));
//功能测试(输入的两个链表有多个节点,节点的值互不相同
//print(of17.mergeRecursive(head3, head4));
//print(of17.merge(head3, head4));
//特殊输入测试(两个链表的一个头节点为NULL指针
//print(of17.mergeRecursive(head1, null));
//print(of17.merge(head1, null));
//特殊输入测试(两个链表的两个头节点为NULL指针
//print(of17.mergeRecursive(null, null));
//print(of17.merge(null, null));
//特殊输入测试(两个链表中只有一个节点
//print(of17.mergeRecursive(node4, null));
print(of17.merge(node4, null));
}
}
功能测试(输入的两个链表有多个节点,节点的值互不相同
1, 2, 3, 4, 5, 6, 7, 8,
功能测试(输入的两个链表有多个节点,节点的值互不相同
2, 2, 4, 4, 6, 6, 8, 8,
特殊输入测试(两个链表的一个头节点为NULL指针
1, 3, 5, 7,
特殊输入测试(两个链表的两个头节点为NULL指针
List is null
特殊输入测试(两个链表中只有一个节点
7,