您的位置:首页 > 其它

怎样实现链表的归并排序

2014-01-27 01:06 281 查看
#返回上一级

@Author: 张海拔

@Update: 2014-01-27

@Link: http://www.cnblogs.com/zhanghaiba/p/3534521.html
链表不像数组通过计算来随机存取,高效的排序算法如快速排序、堆排序都比较难实现,而归并排序就适合给链表排序。

在"有序单链表的合并 link(public)"问题中,我对带头结点和不带头结点的 有序单链表的合并算法做了比较全面的介绍。

知道了 链表的就地合并算法 比数组的合并算法时间效率要好一些,而空间复杂度则完胜,是O(1)(不然怎么叫就地)。

我们已经熟悉数组的“归并排序 link(public) ”,链表的归并排序用到的归并算法肯定是一样的,是典型的分治(二分)过程。

还是先给出伪代码:

merge_sort()

{

if 元素只有一个(已经有序)

  return;

划分为左右两段

对左段进行merge_sort()

对右段进行merge_sort()

//此时左段和右端已经有序

对左段和右段进行sorted_merge()

}

sorted_list_merge()我们已经实现过了,merge_sort的思路也清晰了,剩下的问题是,怎样把一段链表划分为两段

链表的追及问题其实挺经典的,比如“链表是否有环”和“确定链表倒数第K个元素”,这样的问题都是用到追击(相对速度)的思路。

这里也一样,使用一个slow指针和一个fast指针,让fast指针相对slow指针的移动速度是单位1。这样fast走到尽头时,slow就在“中间”位置了。

这个时候需要考虑一些边界问题。

只有一个元素时,merge_sort()直接返回(已经有序,不用再处理),不再划分。

那么只有两个元素的情况对应划分的最小子问题——

首先,直接让slow初始指向first,而fast初始指向first->next,

然后每次前进让fast先后一步再验证是否到边界,若没有则fast和slow都走一步。

这样初始化后,fast走一步后发现到了边界,slow便不走,还是指向first。

将左段链表的首节点指针left = first,右段链表的首节点指针right = slow->next,然后切断原链表,即slow->next = NULL;

对于最小子问题这样划分是正确的(left指向第一个节点,right指向第二个节点)。

由于fast相对slow速度为1,且最小子问题也正确,对于更大的子问题,不管划分边界是[n/2]取上界还是下界问题都不大。

所以,这个算法时间复杂度仍然是O(n*lgn),空间复杂度O(1)

数组的归并排序合并过程需要来回两次循环复制即2*O(n),而链表的归并排序是划分过程仅需要循环一次1*O(n)。

两者递归的深度是一样的(都是二分)。

综合来看,链表归并排序的时间复杂度系数应该更低,即理论上会比数组归并排序更快。

下面给出链表归并排序的完整实现——

其中:

sorted_list_merge_in_place()是递归实现的

sorted_list_mrege_in_place2()是迭代实现的

list_merge_sort()接受带头结点的单链表,返回也是带头结点的单链表

list_merge_sort_core()接受不带头结点的单链表,返回的也是不带头结点的单链表

通过简单包装一下list_merge_sort_core(),即可实现list_merge_sort()

默认支持带头结点的单链表是考虑到问题集的其它链表都是带头结点的,而不带头结点适用性更强,实现了后者,前者只需简单包装就能实现。

约定:带头结点的单链表的头结点指针命名为head,不带头结点的单链表的第一个(首)结点的指针命名为first。带头结点的单链表的第一个有效元素的指针也叫first。

/*
*Author: ZhangHaiba
*Date: 2014-1-26
*File: merge_sort_for_singly_linked_list.c
*
*a demo shows merge sort for singly_linked_list
*/

#include <stdio.h>
#include <stdlib.h>
#define INF 0x7fffffff

typedef struct node * link;
typedef struct node {
int item;
link next;
}node;

//public
/*
*recieve a singly linked list with Head Node and
*return a singly linked list with Head Node as well
*/
link list_merge_sort(link list_head);
link NODE(int item, link next);
link list_create(int n);
void list_travel(link head);
void list_destroy(link head);

//private
/*recieve a singly linked list without Head Node and
*return a singly linked list without Head Node as well
*/
link list_merge_sort_core(link list_first);
void list_divide(link src_list, link *left_list, link *right_list);
link sorted_list_merge_in_place(link src_list_a, link src_list_b);  //recusive
link sorted_list_merge_in_place2(link src_list_a, link src_list_b); //iterative

int main(void)
{
int n;

scanf("%d", &n);
link list_a = list_create(n);
printf("before merge sort, travel:\n");
list_travel(list_a);
list_a = list_merge_sort(list_a);
printf("after merge sort, travel:\n");
list_travel(list_a);
list_destroy(list_a);
return 0;
}

link list_merge_sort(link head)
{
if (head == NULL) //if [list with Head Node] have not Head Node
return NULL;
return NODE( INF, list_merge_sort_core(head->next) );
}

link list_merge_sort_core(link first) //first node pointer
{
link left, right;

if (first == NULL || first->next == NULL)
return first;
list_divide(first, &left, &right);
left = list_merge_sort_core(left);
right = list_merge_sort_core(right);
return sorted_list_merge_in_place2(left, right);
}

//guarantee first != NULL
void list_divide(link first, link *left, link *right)
{
link slow = first, fast = first->next;

while (fast != NULL) {
fast = fast->next;
if (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
}
*left = first;
*right = slow->next;
slow->next = NULL;
}

link sorted_list_merge_in_place(link left, link right)
{
if (left == NULL)
return right;
if (right == NULL)
return left;
link first = NULL;
if (left->item <= right->item) {
first = left;
first->next = sorted_list_merge_in_place(left->next, right);
} else {
first = right;
first->next = sorted_list_merge_in_place(left, right->next);
}
return first;
}

link sorted_list_merge_in_place2(link left, link right)
{
link tmp_head = NODE(INF, NULL);
link first = tmp_head;

for (; left != NULL && right != NULL; first = first->next) {
if (left->item <= right->item)
first->next = left, left = left->next;
else
first->next = right, right = right->next;
}
first->next = left != NULL ? left : right;
first = tmp_head->next;
free(tmp_head);
return first;
}

link NODE(int item, link next)
{
link born = malloc(sizeof (node));
born->item = item;
born->next = next;
return born;
}

//tail insert
link list_create(int n)
{
int i, item;
link head = NODE(INF, NULL);
link tail = head;

for (i = 0; i < n; ++i) {
scanf("%d", &item);
tail->next = NODE(item, NULL);
tail = tail->next;
}
return head;
}

void list_travel(link head)
{
for (head = head->next; head != NULL; head = head->next)
printf(head->next == NULL ? "%d\n" : "%d ", head->item);
}

void list_destroy(link head)
{
head->next == NULL ? free(head) : list_destroy(head->next);
}


测试示范:

ZhangHaiba-MacBook-Pro:code apple$ ./a.out
1
435
before merge sort, travel:
435
after merge sort, travel:
435
ZhangHaiba-MacBook-Pro:code apple$ ./a.out
9
4324 31 515 41 46 43 8 53 3
before merge sort, travel:
4324 31 515 41 46 43 8 53 3
after merge sort, travel:
3 8 31 41 43 46 53 515 4324


#返回上一级
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: