1. 程式人生 > >連結串列和歸併排序(Merge Sort)

連結串列和歸併排序(Merge Sort)

歸併排序適合於對連結串列進行原址排序,即只改變指標的連線方式,不交換連結串列結點的內容。

歸併排序的基本思想是分治法:先把一個連結串列分割成只有一個節點的連結串列,然後按照一定順序、自底向上合併相鄰的兩個連結串列。

只要保證各種大小的子連結串列是有序的,那麼最後返回的連結串列就一定是有序的.

歸併排序分為分割和合並兩個子過程。分割是用遞迴的方法,把連結串列對半分割成兩個子連結串列;合併是在遞迴返回(回朔)的時候,把兩個有序連結串列合併成一個有序連結串列。

繪圖1

(注意:只有一個節點的連結串列一定是有序的)

這裡sort過程就是分割過程;merge過程就是合併且排序的過程

說到分割連結串列,那麼問題來了:連結串列不是隨機訪問的,我怎麼知道分割點在哪裡?一個寶貴的經驗就是:維護兩個指標,一快一慢。快指標每次後移兩個單位,慢指標每次只移動一個單位。當快指標移動到tail或者最後一個有效節點時,慢指標就指向了中間的節點。

sort過程:

Node* sort (Node* beg)
{
    if(beg==tail || beg->next==tail) return beg;
    Node* a = beg; Node* b = beg->next;
    while(b!=tail && b->next != tail)
    {
        a = a->next; b = b->next->next;
    }
    b = a->next;    //the beginning of right part
    a->next = tail; //
the end of left part return merge(sort(beg), sort(b)); }

把連結串列分割之後就要合併。merge操作傳入的引數是兩個有序連結串列,返回的是合併後的有序的連結串列。兩個有序連結串列簡單拼接之後不一定是有序的,需要對每一個元素重排。這個重排的過程是從兩個連結串列各自最小(最大)元素開始,誰小(大)就把誰放到新的連結串列裡。

merge1

Node* LinkedList<T>::merge(Node* a, Node* b)
{
	Node dummy = Node();
	Node* head = &dummy;
	// temp是正在合併的表的節點
	Node* temp = head;
	while(a!=tail && b!=tail) //逐個比較連結串列a和連結串列b的每個元素
	{
		if(a->data <= b->data)
		{
			// 如果a比b小, 那麼當前結點的後繼就是a
			temp->next = a;
			// 把當前節點移向後繼
			temp = a;
			// a後移
			a = a->next;
		}
		else 
		{
			temp->next = b;
			temp = b; 
			b = b->next;
		}
		// 如果原表a已經排完,那麼新表後面就放b的剩餘元素
		// 否則仍然以a為標準和b進行比較
		temp->next = (a==tail) ? b : a;
	}
	return head->next;
}