算法題:合並兩個有序列表


說明:這篇文章是學習交流,轉載請注明出處。歡迎轉載!

             題目:已知有兩個有序的單鏈表,其頭指針分別為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》

版權聲明:本文博主原創文章。博客,未經同意,不得轉載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM