連結串列歸併排序

小弟季義欽發表於2012-11-03
#include <iostream>
using namespace std;
/**
* 對連結串列進行原地排序
* 回憶陣列歸併最重要的就是:找到陣列中點,以及歸併兩個有序陣列。
* 類似地這裡有兩個關鍵點: 
* (1)找到連結串列的中間節點,用一快一慢兩個指標往前走;
* (2)歸併兩個已經排好序的連結串列,指向歸併結果的指標總是變化的,所以需要返回一個指向歸併好的連結串列的頭指標;
* (3)最後注意,將連結串列劃分成兩截的時候,還應該讓前半部分的最後一個節點指向空(截斷),這樣遞迴各自部分就不需要還去
*	   指明到哪個節點結束了。
*/
typedef struct node{
	int value;
	struct node *next;
}NODE;

void printList(NODE *head);
NODE *merge(NODE *a, NODE *b);
void splitList(NODE *head, NODE **list1, NODE **list2);
void mergeSort(NODE **headRef);

/**
* 演算法主體:找到中間節點,截斷,各自遞迴,再將兩部分歸併,同時記錄歸併後的頭指標
*/
void mergeSort(NODE **headRef){
	NODE *head = *headRef;
	NODE *a,*b;

	//當只剩下一個節點或者沒有節點的時候不需要繼續歸併
	if(head == NULL || head->next == NULL) return;

	//將連結串列等長截斷為兩個
	splitList(head, &a, &b);

	//遞迴
	mergeSort(&a);
	mergeSort(&b);
	
	//歸併
	*headRef = merge(a, b);
}

/**
* 找到當前連結串列head的中間節點,並將連結串列截斷
* 前半部分通過list1返回,後半部分通過list2返回
*/
void splitList(NODE *head, NODE **list1, NODE **list2){
	NODE *p,*q;
	p = head;
	q = head->next;

	//沒有或者只有一個節點
	if(head == NULL || head->next == NULL){
		*list1 = head;
		*list2 = NULL;
	}else{

		//一快一慢直到快指標走完連結串列
		while(q != NULL){
			q = q->next;
			if(q != NULL){
				p = p->next;
				q = q->next;
			}			
		}

		//返回兩半各自頭指標
		*list1 = head;
		*list2 = p->next;

		//截斷
		p->next = NULL;
	}
}

/**
* 歸併兩個有序連結串列,採用遞迴方式解決(很明顯有子問題)
*/
NODE *merge(NODE *a, NODE *b){
	NODE *head;

	if(a == NULL && b == NULL) return NULL;
	if(a == NULL) return b;
	if(b == NULL) return a;

	if(a->value < b->value){
		head = a;
		head->next = merge(a->next, b); //遞迴
	}
	else{
		head = b;
		head->next = merge(a, b->next); //遞迴
	}
	
	return head;
}

int main(){
	int a[] = {3,5,1,1,6,12,2,7,9,10,14,12,12,3,9};
	NODE *head,*p;
	
	//建立連結串列頭
	p = (NODE *)malloc(sizeof(NODE));
	p->value = a[0];
	p->next = NULL;
	head = p;

	//構建連結串列
	for(int i=1;i<15;i++){
		p = (NODE *)malloc(sizeof(NODE));
		p->value = a[i];
		p->next = head->next;
		head->next = p;
	}

	//開始歸併排序
	p = head;
	while(p->next != NULL) p = p->next;
	mergeSort(&head);

	//列印結果
	p = head;
	while(p->next != NULL){
		cout<<p->value<<",";
		p = p->next;
	}
	cout<<endl;
	return 0;
}

/**
* 列印連結串列
*/
void printList(NODE *head){
	NODE *p = head;

	if(head == NULL) return;
	while(p->next != NULL){
		cout<<p->value<<",";
		p = p->next;
	}
	cout<<endl;
}


相關文章