C++演算法之 合併兩個有序連結串列
題目:合併兩個已經排序好的連結串列
方法1:
兩個連結串列
比如連結串列1: 1->3->5->7->9
連結串列2: 2->4->6->8->10
跟我們合併兩個陣列一樣,連結串列1的頭結點 和連結串列2的頭節點比較,如果連結串列1頭節點的值大於連結串列2頭接點的值,
那麼連結串列2的頭結點為合併連結串列的頭結點,那麼連結串列1的頭節點繼續和連結串列2的第二個節點(剩餘連結串列2的頭結點)
作比較,但一個連結串列遍歷完之後,如果另外一個連結串列還沒有遍歷完,因為連結串列本來就是排序的,所以讓合併連結串列的
尾巴節點指向未遍歷完連結串列的頭結點就可以
舉個例子:
連結串列1: 1,3,5,23,34;
連結串列2: 2,4,6,8,10;
當遍歷之後 連結串列3:1,2,3,4,8,10 此時連結串列2已經遍歷完,while迴圈退出,但是剩餘連結串列1還有 23,34
此時 讓連結串列3的尾巴節點10 連結 剩餘連結串列的頭節點 23 就可以了
<span style="color:#000000;">// 合併連結串列.cpp : 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
struct ListNode
{
int m_Data;
ListNode* m_pNext;
ListNode(int value,ListNode* next = NULL):m_Data(value),m_pNext(next){}
};
/*
兩個連結串列 比如連結串列1: 1->3->5->7->9
連結串列2: 2->4->6->8->10
跟我們合併兩個陣列一樣,連結串列1的頭結點 和連結串列2的頭節點比較,如果連結串列1頭節點的值大於連結串列2頭接點的值,
那麼連結串列2的頭結點為合併連結串列的頭結點,那麼連結串列1的頭節點繼續和連結串列2的第二個節點(剩餘連結串列2的頭結點)
作比較,但一個連結串列遍歷完之後,如果另外一個連結串列還沒有遍歷完,因為連結串列本來就是排序的,所以讓合併連結串列的
尾巴節點指向未遍歷完連結串列的頭結點就可以
舉個例子:
連結串列1: 1,3,5,23,34;
連結串列2: 2,4,6,8,10;
當遍歷之後 連結串列3:1,2,3,4,8,10 此時連結串列2已經遍歷完,while迴圈退出,但是剩餘連結串列1還有 23,34
此時 讓連結串列3的尾巴節點10 連結 剩餘連結串列的頭節點 23 就可以了
*/
ListNode* MergeList2(ListNode* head1,ListNode* head2)
{
if (head1 == NULL)
{
return head2;
}
else if(head2 == NULL)
{
return head1;
}
ListNode* MergeHead = NULL;
if (head1->m_Data < head2->m_Data)
{
MergeHead = head1;
head1 = head1->m_pNext;
}
else
{
MergeHead = head2;
head2 = head2->m_pNext;
}
ListNode* tmpNode = MergeHead;
while (head1&&head2)
{
if (head1->m_Data < head2->m_Data)
{
MergeHead->m_pNext = head1;
head1 = head1->m_pNext;
}
else
{
MergeHead->m_pNext = head2;
head2 = head2->m_pNext;
}
MergeHead = MergeHead->m_pNext;
}
if (head1)
{
MergeHead->m_pNext = head1;
}
if (head2)
{
MergeHead->m_pNext = head2;
}
return tmpNode;
}
int _tmain(int argc, _TCHAR* argv[])
{
ListNode* pHead1 = new ListNode(1);
ListNode* pCur = pHead1;
for (int i = 3; i < 10; i+=2)
{
ListNode* tmpNode = new ListNode(i);
pCur->m_pNext = tmpNode;
pCur = tmpNode;
}
ListNode* pHead2 = new ListNode(2);
pCur = pHead2;
for (int j = 4; j < 10; j+=2)
{
ListNode* tmpNode = new ListNode(j);
pCur->m_pNext = tmpNode;
pCur = tmpNode;
}
ListNode* head = MergeList2(pHead1,pHead2);
while (head)
{
cout<<head->m_Data<<" ";
head=head->m_pNext;
}
getchar();
return 0;
}</span>
方法2:
/*
我們分析兩個連結串列的過程,首先從合併兩個連結串列的頭結點開始,連結串列1的頭節點的值小於連結串列2的頭結點的值,因此連結串列1的頭結點
就是合併連結串列的頭節點,繼續合併剩下的連結串列,在兩個連結串列中剩餘的節點仍然是排序的,因此合併兩個連結串列的步驟是一樣的,我們還是比較兩個頭結點的
值,此時連結串列2的頭結點的值小於連結串列1的頭結點的值,因此連結串列2的頭結點是合併剩餘連結串列的頭結點,我們把這個節點和前面合併連結串列時得到的連結串列的尾巴節點
連結起來
按照上面的分析可知:每次合併的步驟都是一樣的,由此我們想到了遞迴。
*/
// 合併連結串列.cpp : 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
struct ListNode
{
int m_Data;
ListNode* m_pNext;
ListNode(int value,ListNode* next = NULL):m_Data(value),m_pNext(next){}
};
/*
我們分析兩個連結串列的過程,首先從合併兩個連結串列的頭結點開始,連結串列1的頭節點的值小於連結串列2的頭結點的值,因此連結串列1的頭結點
就是合併連結串列的頭節點,繼續合併剩下的連結串列,在兩個連結串列中剩餘的節點仍然是排序的,因此合併兩個連結串列的步驟是一樣的,我們還是比較兩個頭結點的
值,此時連結串列2的頭結點的值小於連結串列1的頭結點的值,因此連結串列2的頭結點是合併剩餘連結串列的頭結點,我們把這個節點和前面合併連結串列時得到的連結串列的尾巴節點
連結起來
按照上面的分析可知:每次合併的步驟都是一樣的,由此我們想到了遞迴。
*/
ListNode* MergeList(ListNode* pHead1,ListNode* pHead2)
{
if (pHead1 == NULL)
{
return pHead2;
}
else if (pHead2 == NULL)
{
return pHead1;
}
ListNode* pMergeHead = NULL;
if (pHead1->m_Data < pHead2->m_Data)
{
pMergeHead = pHead1;
pMergeHead->m_pNext = MergeList(pHead1->m_pNext,pHead2);
}
else
{
pMergeHead = pHead2;
pMergeHead->m_pNext = MergeList(pHead1,pHead2->m_pNext);
}
return pMergeHead;
}
int _tmain(int argc, _TCHAR* argv[])
{
ListNode* pHead1 = new ListNode(1);
ListNode* pCur = pHead1;
for (int i = 3; i < 10; i+=2)
{
ListNode* tmpNode = new ListNode(i);
pCur->m_pNext = tmpNode;
pCur = tmpNode;
}
ListNode* pHead2 = new ListNode(2);
pCur = pHead2;
for (int j = 4; j < 10; j+=2)
{
ListNode* tmpNode = new ListNode(j);
pCur->m_pNext = tmpNode;
pCur = tmpNode;
}
ListNode* head = MergeList2(pHead1,pHead2);
while (head)
{
cout<<head->m_Data<<" ";
head=head->m_pNext;
}
getchar();
return 0;
}
看了這道題目,那麼上次的合併陣列也可以用遞迴這裡附上程式碼:
<span style="color:#000000;">// MergeArray.cpp : 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
//遞迴方法
void MergeArray2(int a[],int aCount,int b[],int blen)
{
int len = aCount+blen-1;
aCount--;
blen--;
if (aCount < 0)
{
while (blen>=0)
{
a[len--] = b[blen--];
}
return;
}
if (a[aCount] > b[blen])
{
a[len] = a[aCount];
MergeArray2(a,aCount,b,++blen);
}
else
{
a[len] = b[blen];
MergeArray2(a,++aCount,b,blen);
}
}
void MergeArray(int a[], int aCount, int b[], int blen)//aCount為a陣列實際(狹義)長度,blen為b陣列實際長度
{
int len = aCount + blen - 1;//合併陣列的長度也就是a陣列的廣義長度
aCount--;
blen--;
while (aCount>=0 && blen>=0)
{
if (a[aCount] >= b[blen])
{
a[len--] = a[aCount--];
}
else
{
a[len--] = b[blen--];
}
}
while(blen >= 0)
{
a[len--] = b[blen--];
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int a[] = {2,4,6,8,10,0,0,0,0,0};
int b[] = {1,3,5,7,9};
MergeArray2(a,5,b,5);
for (int i = 0; i < sizeof(a)/sizeof(a[0]); i++)
{
cout<<a[i]<<" ";
}
getchar();
return 0;
}</span>
個人感覺合併陣列用遞迴不太好,因為考慮如果一個數組遍歷完另一個數組還沒有遍歷完這個情況有點麻煩,而如果是連結串列的話,一個數連結串列歷完,
那麼這個連結串列為空,則返回另外一個連結串列就可以了,也就是前面合併好的連結串列自動連結上另外沒有遍歷完的連結串列的那部分!