演算法題:合併兩個有序的連結串列
阿新 • • 發佈:2019-01-23
說明:本文僅供學習交流,轉載請標明出處,歡迎轉載!
題目:已知有兩個有序的單鏈表,其頭指標分別為head1和head2,實現將這兩個連結串列合併的函式:
Node* ListMerge(Node *head1,Node *head2)
這個演算法很像我們排序演算法中的歸併排序,只能說“很像”,因為思想是一樣的,但是這個與歸併排序還是有區別的,區別如下:
1.歸併排序是針對有序陣列,而這裡是有序連結串列;
2.歸併排序排序的時間複雜度為o(nlogn),而這裡的時間複雜度最壞情況下為O(m+n),最好的情況下為O(min{m,n})。
3.歸併排序需要重新申請空間,而這裡無需再重新申請空間,只需改變連結串列結點的指標指向。
而這裡演算法的思想跟歸併排序是一樣的,都是對兩個待歸併的線性表分別設定1個指標,比較這兩個當前指標的大小,將小的結點加入到合併後的線性表中,並向後移動當前指標。若兩個線性表中,至少有一個表掃描完,走將對應的另一個表之間整體新增到合併後的線性表中。在這裡:連結串列和陣列的區別在於,連結串列只需要改變當前合併序列尾指標的位置,而陣列則要將剩下的值依次複製到歸併表的尾部。
演算法的遞迴實現如下:
Node *ListMerge1(Node *head1,Node *head2)//採用遞迴的方法實現 { if(head1==NULL) return head2; if(head2==NULL) return head1; Node *head=NULL; if(head1->value < head2->value) { head=head1; head->next=ListMerge1(head1->next,head2); } else { head=head2; head->next=ListMerge1(head1,head2->next); } return head; }
演算法的非遞迴實現如下:
Node *ListMerge(Node *head1,Node *head2) { if(!head1) return head2; if(!head2) return head1; Node *head=NULL;//合併後的頭指標 Node *p1=head1;//p1用於掃描連結串列1 Node *p2=head2;//p2用於掃描連結串列2 if(head1->value<head2->value) { head=head1; p1=head1->next; } else { head=head2; p2=head2->next; } Node *p=head;//p永遠指向最新合併的結點 while(p1 && p2)//如果迴圈停止,則p1或p2至少有一個為NULL { if(p1->value<p2->value) { p->next=p1; p1=p1->next; } else { p->next=p2; p2=p2->next; } p=p->next; } if(p1)//如果鏈1還沒走完 { p->next=p1; } else if(p2)//如果鏈2還沒走完 { p->next=p2; } return head; }
整個測試程式碼如下:
#include<iostream>
using namespace std;
struct Node
{
int value;
Node* next;
Node(int v):value(v){}
};
/*建立一個連結串列,1->2->3->4->5->6->7*/
Node* CreateList1()//建立一個有序的單鏈表1
{
Node *head;
Node *n1=new Node(1);
Node *n3=new Node(3);
Node *n5=new Node(5);
Node *n7=new Node(7);
Node *n9=new Node(9);
head=n1;
n1->next=n3;
n3->next=n5;
n5->next=n7;
n7->next=n9;
n9->next=NULL;
return head;
}
Node* CreateList2()//建立一個有序的單鏈表2
{
Node *head;
Node *n2=new Node(2);
Node *n4=new Node(4);
Node *n6=new Node(6);
Node *n8=new Node(8);
head=n2;
n2->next=n4;
n4->next=n6;
n6->next=n8;
n8->next=NULL;
return head;
}
void FreeList(Node *head)//將連結串列空間釋放
{
if(head==NULL)
{
return ;
}
else
{
Node *temp=head->next;
delete head;
head=temp;
FreeList(head);
}
}
void VisitList(Node *head)//遍歷連結串列中的元素,用遞迴的方法遍歷
{
if(head)
{
cout<<head->value<<"->";
VisitList(head->next);
}
else
{
cout<<"null"<<endl;
}
}
Node *ListMerge(Node *head1,Node *head2)
{
if(!head1) return head2;
if(!head2) return head1;
Node *head=NULL;//合併後的頭指標
Node *p1=head1;//p1用於掃描連結串列1
Node *p2=head2;//p2用於掃描連結串列2
if(head1->value<head2->value)
{
head=head1;
p1=head1->next;
}
else
{
head=head2;
p2=head2->next;
}
Node *p=head;//p永遠指向最新合併的結點
while(p1 && p2)//如果迴圈停止,則p1或p2至少有一個為NULL
{
if(p1->value<p2->value)
{
p->next=p1;
p1=p1->next;
}
else
{
p->next=p2;
p2=p2->next;
}
p=p->next;
}
if(p1)//如果鏈1還沒走完
{
p->next=p1;
}
else if(p2)//如果鏈2還沒走完
{
p->next=p2;
}
return head;
}
Node *ListMerge1(Node *head1,Node *head2)//採用遞迴的方法實現
{
if(head1==NULL)
return head2;
if(head2==NULL)
return head1;
Node *head=NULL;
if(head1->value < head2->value)
{
head=head1;
head->next=ListMerge1(head1->next,head2);
}
else
{
head=head2;
head->next=ListMerge1(head1,head2->next);
}
return head;
}
int main()
{
Node *head1=CreateList1();
Node *head2=CreateList2();
cout<<"歸併前"<<endl;
cout<<"連結串列1:";
VisitList(head1);
cout<<"連結串列2:";
VisitList(head2);
cout<<"合併後的連結串列:";
//Node *head=ListMerge(head1,head2);
Node *head=ListMerge1(head1,head2);
VisitList(head);
FreeList(head);
return 0;
}
測試結果如下:
參考資料-------------《劍指offer》