链表练习2——有序链表合并
1. 题目
输入两个递增排序的链表,合并这两个链表并使新链表的节点仍然是递增的。例如下图链表1和链表2,则合并
之后的升序链表如链表3所示。链表节点定义如下
struct ListNode
{
int m_nValue;
ListNode* m_pNext;
}
2 题析
解题思路分两步:
1)明确合并过程
2)代码鲁棒性的考虑,即需考虑特殊情况,如链表1空,链表2非空,或反过来,或两者都空
分析如下:
1)合并过程
a)两个链表1,2首节点值比较,确定出链表3的首节点
b)继续合并两个链表中剩余节点,如(a)比较,筛选出第2个节点
c)可发现,这是一个递归过程,如下图所示
2)代码鲁棒性
当链表1为空链表时,应返回链表2,反之,链表2为空时,返回链表1
链表1和链表2都为空时,返回空链表
想清楚合并过程和特殊情况后,就可以动手写代码了
3 题解(C++实现)
ListNode* MergeSortedLists(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == nullptr)
return pHead2;
else if(pHead2 == nullptr)
return pHead1;
ListNode* pMergeHead = nullptr;
if(pHead1->m_nValue < pHead2->m_nValue)
{
pMergedHead = pHead1;
pMergedHead->m_pNext = MergeSortedLists(pHead1->m_pNext, pHead2);
}else
{
pMergedHead = pHead2;
pMergedHead->m_pNext = MergeSortedLists(pHead1, pHead2->m_pNext);
}
return pMergedHead;
}
4 测试用例与测试结果
// ====================测试代码====================
ListNode* Test(char* testName, ListNode* pHead1, ListNode* pHead2)
{
if(testName != nullptr)
printf("%s begins:\n", testName);
printf("The first list is:\n");
PrintList(pHead1);
printf("The second list is:\n");
PrintList(pHead2);
printf("The merged List is:\n");
ListNode* pMergedHead = MergeSortedLists(pHead1, pHead2);
PrintList(pMergedHead);
printf("\n\n");
return pMergedHead;
}
// Test1: 两个不同链表的合并
void Test1()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode5 = CreateListNode(5);
connectListNodes(pNode1, pNode3);
connectListNodes(pNode3, pNode5);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode6 = CreateListNode(6);
connectListNodes(pNode2, pNode4);
connectListNodes(pNode4, pNode6);
ListNode* pMergedHead = Test("Test1", pNode1, pNode2);
DestroyList(pMergeHead);
}
// Test2:合并两个相同的链表
void Test2()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode5 = CreateListNode(5);
connectListNodes(pNode1, pNode3);
connectListNodes(pNode3, pNode5);
ListNode* pNode2 = CreateListNode(1);
ListNode* pNode4 = CreateListNode(3);
ListNode* pNode6 = CreateListNode(5);
connectListNodes(pNode2, pNode4);
connectListNodes(pNode4, pNode6);
ListNode* pMergedHead = Test("Test2", pNode1, pNode2);
DestroyList(pMergeHead);
}
// Test3:两个链表只有一个节点
void Test3()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pMergedHead = Test("Test3", pNode1, pNode2);
DestroyList(pMergedHead);
}
// Test4:其中一个是空链表
void Test4()
{
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode5 = CreateListNode(5);
connectListNodes(pNode1, pNode3);
connectListNodes(pNode3, pNode5);
ListNode* pMergedHead = Test("Test4", pNode1, nullptr);
DestroyList(pMergedHead);
}
// Test5:两个为空链表
void Test5()
{
ListNode* pMergedHead = Test("Test5", nullptr, nullptr);
}
int main(int argc, char* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5();
return 0;
}
测试结果
以上测试都没有问题
一点小结:
合并链表难点在于找出规律——即链表合并过程的共性。这里是“递归”(当然,不用递归也可以)
其次,注意代码的鲁棒性,即考虑特殊情况下代码是否会崩溃
参考资料:
《剑指offer》第2版——第三章
本节代码可参考:https://github.com/zhedahht/CodingInterviewChinese2/tree/master/25_MergeSortedLists