题目:两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的!如下图所示:
注意问题
这是一个经常被各公司采用的面试题,最容易犯两种错误:
- 一是在写代码时没有对合并的过程想清楚,导致合并出来的链表不是想要的结果!
- 二是代码的鲁棒性存在问题,程序一旦有特殊的链表就会崩溃
合并思路
1、先考虑考虑特殊情况,包括输入空链表,两条链表是同一条链表等等情况需要在一开始就考虑好
2、确定新链表的头结点,谁小就把谁当做头结点
3、使用一个指针来维护尾节点,方便尾部插入元素,每插入一个元素就向后移动
4、谁小就把谁插到尾部,然后继续比较
5、注意其中任何一条链表结束不要忘记另一条还没有结束的链表,要在尾部补上
代码
//合并两个有序链表,合并后依然有序
pList Merge(pList list1, pList list2)
{
pList newlist = NULL;
//tail表示链表的尾部
pNode tail = NULL;
//先考虑特殊情况
if (list1 == list2)
return NULL;
if (list1 == NULL)
return list2;
if (list2 == NULL)
return list1;
//确定头节点
if (list1->data < list2->data)
{
newlist = list1;
list1 = list1->next;
}
else{
newlist = list2;
list2 = list2->next;
}
//两条链表中找较小的元素尾插
tail = newlist;
while ((list1 != NULL) && (list2 != NULL))
{
if (list1->data < list2->data){
tail->next = list1;
list1 = list1->next;
}
else{
tail->next = list2;
list2 = list2->next;
}
tail = tail->next;
}
//能走到这里说明其中一条链表已经结束了
if (list1 == NULL)
{
tail->next = list2;
}
else{
tail->next = list1;
}
return newlist;
}
测试代码
void test()
{
int i = 0;
pList plist1 = NULL;
pList plist2 = NULL;
pList plist3 = NULL;
InitList(&plist1);
InitList(&plist2);
for (i = 0; i <= 10; i++)
{
if (i % 2 == 0)
PushBack(&plist1, i);
else
{
PushBack(&plist2, i);
}
}
PushBack(&plist1, 12);
PushBack(&plist1, 13);
PrintList(plist1);
PrintList(plist2);
plist3 = Merge(plist1, plist2);
PrintList(plist3);
}
递归的方式更易理解
其实很容易看出来,只要确定了第一个节点,我们可以把它看成是第一个节点与剩下两条链表的合并结果的合并,这样的话只需要有限次的递归便可以完成这个看似复杂的问题!
//递归写法
pNode Merge_R(pList list1, pList list2)
{
pList newlist = NULL;
//先考虑特殊情况
if (list1 == list2)
return NULL;
if (list1 == NULL)
return list2;
if (list2 == NULL)
return list1;
//确定头结点
if (list1->data < list2->data)
{
newlist = list1;
list1->next = Merge_R(list1->next, list2);
}
else
{
newlist = list2;
list2->next = Merge_R(list2->next, list1);
}
return newlist;
}
考点
- 考察分析问题能力,考察指针操作能力,应该透彻分析问题形成清晰的思路,才能够写出正确的代码!
- 考察代码的鲁棒性,需要考虑到很多特殊操作,尤其是空指针的情况和空链表的情况处理!