归并排序是效率非常高的一种排序方式,和快速排序一样用了分治的思想。分治法的精髓在于将一个复杂问题分割成为多个简单的子问题,然后将子问题逐个解决,最终合并在一起以后就是复杂问题的解了。
这篇文章主要讲归并排序链表的实现方式,如果想了解数组的归并排序,可移步这里。
归并排序的思想其实挺简单的,简而言之就是将数列分割成子序列,先对子序列进行排序,再将排好序的子序列合并成一个完整的数列。具体例子如下:
[3 7 6 4 8 9 2 1]
/ \
分割 [3 7 6 4] [8 9 2 1]
/ \ / \
分割 [3 7] [6 4] [8 9] [2 1]
/ \ / \ / \ / \
分割 [3] [7] [6] [4] [8] [9] [2] [1]
\ / \ / \ / \ /
合并 [3 7] [4 6] [8 9] [1 2]
\ / \ /
合并 [3 4 6 7] [1 2 8 9]
\ /
合并 [1 2 3 4 6 7 8 9]
分割时从数列中间开始,将数列分成两部分,然后对分割后的序列继续进行分割直到分割不能再分为止。然后对已经排好序的子序列进行合并,重新产生一个排序好的数列。
用链表进行归并排序和数组排序略有不同,但是思想还是一样的。唯一的难点在于如何找到链表的中间点,这里就要运用到了一个额外的技巧——快慢指针。快慢指针常常用于判断链表中是否有环,简单来说就是有两个指针,快指针每次走两个节点,慢指针每次走一个节点,当快指针走到链表尽头时,慢指针才走到了链表的一半,此时就是我们想要的中间节点。
看代码:
//链表结构单位
struct Node{
int val;
Node* next;
};
//node -- 链表表头
Node* MergeSort(Node* node){
//先判断链表长度是否大于1,小于1时无须排序
if(node!=NULL&&node->next!=NULL){
//运用快慢指针,找到链表的中间节点
Node *fast=node->next;
Node *slow=node;
while(fast!=NULL&&fast->next!=NULL){
fast=fast->next->next;
slow=slow->next;
}
//将链表分成两部分进行分割
Node *p1=MergeSort(slow->next);
slow->next=NULL; //这儿很重要,仔细想想为什么
Node *p2=MergeSort(node);
//对两条子链进行归并
Node *p0=(Node *)malloc(sizeof(Node));
Node *p=p0;
while(p1!=NULL&&p2!=NULL){
if(p1->val<p2->val){
p->next=p1;
p1=p1->next;
}else{
p->next=p2;
p2=p2->next;
}
p=p->next;
}
if(p1!=NULL){
p->next=p1;
}
if(p2!=NULL){
p->next=p2;
}
p=p0->next;
free(p0);
return p;
}
return node;
}
增加个C++版本的完整实现:
#include <iostream>
using namespace std;
//链表结构体
struct List{
int val;
List * next;
List(int value = 0):
val(value),
next(nullptr)
{
}
};
//创建链表
List * createList(int length){
List *root = new List(rand()%100);
List *node = root;
for(int i=0;i<length;++i){
node->next = new List(rand()%100);
node = node->next;
}
return root;
}
//打印链表
void printList(List * root){
while(root!=nullptr){
cout<<root->val<<" ";
root = root->next;
}
cout<<endl;
}
//删除链表
void deletList(List * root){
List * temp;
while(root!=nullptr){
temp = root->next;
delete root;
root = temp;
}
}
//合并链表
List * merge(List * left,List * right){
if(left == nullptr)
return right;
if(right == nullptr)
return left;
if(left->val < right->val){
left->next = merge(left->next,right);
return left;
}else{
right->next = merge(left,right->next);
return right;
}
}
//归并排序
List * mergeSort(List * root){
//空链表或者长度为1的链表直接返回
if(root == nullptr || root->next == nullptr)
return root;
//快慢指针寻找链表中间节点
List *fast = root->next;
List *slow = root;
while(fast!=nullptr&&fast->next!=nullptr){
slow = slow->next;
fast = fast->next->next;
}
//将链表分成左右两段,分别进行归并排序
List * right = mergeSort(slow->next);
slow->next = nullptr;
List * left = mergeSort(root);
//合并左右链表
return merge(left, right);
}
int main(){
srand((unsigned)time(0));
List * root = createList(10);
printList(root);
root = mergeSort(root);
printList(root);
deletList(root);
return 0;
}