當我們需要對鏈表進行排序時,由於不能對它的元素進行隨機訪問,所以更適合使用歸併排序,大名鼎鼎的快速排序用到鏈表上,效率也很低,原因還是在於不能對鏈表中的元素進行隨機訪問,同理,採用堆排序更是不可能的事情。
演算法具體實現時需要一個指向前端節點(鏈表的第一個節點,鏈表中不包含額外的一個節點來作前端節點)的指標,這是因為在演算法實現的時候,不大可能第一個節點正好就是所有元素中最小的一個,則鏈表的前端節點會改變,因此我們需要一個指向前端節點的指標來儲存不斷變化的前端節點。
演算法思想:
MergeSort(headRef)
1) If head is NULL or there is only one element in the Linked List then return.2) Else divide the linked list into two halves. FrontBackSplit(head, &a, &b); /* a and b are two halves */3) Sort the two halves a and b. MergeSort(a); MergeSort(b);4) Merge the sorted a and b (using SortedMerge() discussed here) and update the head pointer using headRef. *headRef = SortedMerge(a, b);
程式碼範例:
#include <stdio.h>#include <stdlib.h>/*Link list node*/struct node{int data;struct node* next;};/*function prototype */struct node* SortedMerge(struct node* a, struct node* b);void FrontBackSplit(struct node* source, struct node** frontRef, struct node** backRef);/*sorts the linked list by changing next pointers(not data) */void MergeSort(struct node** headRef){struct node* head = *headRef;struct node* a;struct node* b;/*base case-- length 0 or 1 */if((head == NULL) || (head->next == NULL)){return;}/*Split head into 'a' and 'b' sublists */FrontBackSplit(head, &a, &b);/*Recursively sort the sublists */MergeSort(&a);MergeSort(&b);/* answer = merge the two sorted lists together */*headRef = SortedMerge(a, b);}struct node* SortedMerge(struct node* a, struct node* b){struct node* result = NULL;/* Base cases */if(a == NULL)return (b);else if(b == NULL)return (a);/* Pick either a or b recur */if(a->data <= b->data){result = a;result->next = SortedMerge(a->next, b);}else{result = b;result->next = SortedMerge(a, b->next);}return (result);}/* UTILITY FUNCTIONS *//* Split the nodes of the given list into front and back halves, and return the two lists using the references parameters. If the length is odd, the extra node shold go in the front list. Uses the fast/slow pointer strategy. */void FrontBackSplit(struct node* source, struct node** frontRef, struct node** backRef){struct node* fast;struct node* slow;if(source == NULL || source->next == NULL){*frontRef = source;*backRef = NULL;}else{slow = source;fast = source->next;/* Advance 'fast' two nodes, and advance 'slow' one node */while(fast != NULL){fast = fast->next;if( fast != NULL ){slow = slow->next;fast = fast->next;}}*frontRef = source;*backRef = slow->next;slow->next = NULL;}}/*Function to print nodes in a given linked list*/void printList(struct node* node){while( node != NULL ){printf("%d ", node->data);node = node->next;}}/* Function to insert a node at the begining of the linked list*/void push(struct node** head_ref, int new_data){/*allocate node*/struct node* new_node = (struct node*)malloc(sizeof(struct node));/*put in the data*/new_node->data = new_data;/*link the old list off the new node*/new_node->next = (*head_ref);/*move the head to point to the new node*/(*head_ref) = new_node;}/* Drier program to test above functions*/int main(){/* Start with the empty list */struct node* res = NULL;struct node* a = NULL;/* Let us create a unsorted linked lists to test the functions Created lists shall be a: 2->3->20->5->10->15 */push(&a, 15);push(&a, 10);push(&a, 5);push(&a, 20);push(&a, 3);push(&a, 2); /* Sort the above created Linked List */MergeSort(&a);printf("\n Sorted Linked List is: \n");printList(a); return 0;}時間複雜度為O(nLogn)。
貌似MergeSort的時間複雜度為O(nLogn),Split的時間複雜度也為O(nLogn)?當然了,總的時間複雜度還是O(nLogn),但是肯定沒有對數組進行歸併排序快。
原文地址:http://www.geeksforgeeks.org/archives/7740