演算法-合併兩個排序的連結串列
題目:
輸入兩個遞增排序的連結串列,合併著兩個連結串列並使新連結串列中的結點仍然是按照遞增順序的。例如輸入的連結串列1和連結串列2如下,合併後的為連結串列3。連結串列的結點定義如下:
struct ListNode
{
int value;
ListNode *next;
};
解題思路:
首先可以確定的是,連結串列1和連結串列2本身就是遞增的,所以合併的過程可以從連結串列1,2的頭結點開始,先比較1,2的頭結點中值的大小,將小的值的結點(比如為連結串列1頭結點)作為合併後的連結串列(連結串列3)的頭結點。隨後可以考慮成連結串列1的從原連結串列第二個結點開始,再次重複上面的步驟,這樣就變成了一個遞迴問題。
程式碼實現:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == NULL)
return pHead2;
else if(pHead2 == NULL)
return pHead1;
ListNode* pMergedHead = NULL;
if(pHead1->value < pHead2->value)
{
pMergedHead = pHead1;
pMergedHead-> next = Merge(pHead1->next, pHead2);
}
else
{
pMergedHead = pHead2;
pMergedHead->next = Merge(pHead1, pHead2->next);
}
return pMergedHead;
}
測試例程:
#include<iostream>
using namespace std;
struct ListNode
{
int value;
ListNode *next;
};
void ShowList(ListNode* L);
void CreateList(ListNode * L,int n,int initial);
ListNode* Merge(ListNode* pHead1, ListNode* pHead2);
int main()
{
ListNode * L1 = new ListNode;
L1->next = nullptr;
ListNode * L2 = new ListNode;
L2->next = nullptr;
CreateList(L1,4,1);
CreateList(L2,4,2);
ShowList(L1);
ShowList(L2);
ListNode * L3 = Merge(L1,L2);
ShowList(L3);
getchar();
return 0;
}
void CreateList(ListNode * L,int n,int initial)
{
L->value = initial;//輸入第一個結點的資料值
n--;
for (int i = 0; i < n; i++)
{
initial = initial+2;
ListNode * p = new ListNode;
p->value = initial;
p->next = nullptr;
L->next = p;
L = p;
}
}
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if(pHead1 == NULL)
return pHead2;
else if(pHead2 == NULL)
return pHead1;
ListNode* pMergedHead = NULL;
if(pHead1->value < pHead2->value)
{
pMergedHead = pHead1;
pMergedHead->next = Merge(pHead1->next, pHead2);
}
else
{
pMergedHead = pHead2;
pMergedHead->next = Merge(pHead1, pHead2->next);
}
return pMergedHead;
}
void ShowList(ListNode* L)
{
ListNode *p =L;
while (p)
{
cout<<p->value<<' ';
p=p->next;
}
cout<<endl;
}
結果如下:
個人感覺值得注意的地方有下面幾個:
(1)如果連結串列1,2為空,要考慮程式碼的魯棒性。
(2)要考慮連結串列1,2中某結點的數值相等的情況,這個在else中包含了。
(3)遞迴呼叫何時退出?
遞迴退出的條件與為了防止空連結串列造成異常的判斷是一個:
if(pHead1 == NULL)
return pHead2;
else if(pHead2 == NULL)
return pHead1;
這就是這個程式碼很巧妙的地方,往往使一行程式碼兩個甚至多個作用,我們舉這樣的例子:
連結串列1 : 1 3
連結串列2 : 2 4
首先執行Merge(1,2)函式,進入if,
進入第一次遞迴,執行Merge(3,2),顯然會進入else,
進入第二次遞迴,執行Merge(3,4),顯然會進入if,
進入第三次遞迴,執行Merge(NULL,4),此時就進入了是否為空的判斷,並返回4,同時遞迴結束。
(4)新的連結串列何時連結?
我們可以這樣理解這件事,還是上面的例子:
連結串列1 : 1 3
連結串列2 : 2 4
程式碼會第一次進入後再遞迴三次,遞迴結束後要return四次(從裡向外),每一次return時都會將連結串列向前連結一個結點,每一次return的結果其實是這樣:
1. 4
2. 3 4
3. 2 3 4
4.1 2 3 4