`

单链表归并排序

 
阅读更多

 

单链表归并排序-O(nlogn)

 
由 王宇 原创并发布:
  • 代码采用Doxygen注释
  • 在以下环境中编译运行通过:Linux Puppy Tahr 6.0.5 kernel 3.14.56 gcc 4.8.4
 
/**
 * @file link_list_sort.c
 *
 * @brief 单链表的归并排序,需要保证O(nlgn)
 *
 * 思路:采用二分法,和归并操作,
 * 及将单链平分成两组,归并操作后合并成一个链。采用递归,重复以上操作。
 *
 * @author WangYu <wangyuxxx@163.com> 
 * @data   2016-09-27
 * @version 1.0
 */

#include<stdio.h>
#include<stdlib.h>

typedef struct LinkList_s {
    int               value; /// 值  
    struct LinkList_s *next; /// 单链的下一个节点

}LinkList_t;


/**
 * @brief 从单链表的中间分成 a, b 两组
 *
 * 思路:分别采用快慢步长,快步长是慢的两倍,当快步长到达链表尾部时,慢步长刚好在链表中间
 *
 * @param [in]  head 单链头 
 * @param [out] a组 b组
 * @retval 空
 */
void getMiddle(LinkList_t *head, LinkList_t **a, LinkList_t **b) {
    LinkList_t *fast = NULL;
    LinkList_t *slow  = NULL;
    LinkList_t *previous;
    
    if (head != NULL && head->next != NULL) {
        fast = head;
        slow  = head;

        while (slow != NULL && fast != NULL) {

            if (slow->next == NULL) {
                break; 
            }

            previous = slow;
            slow = slow->next; 


            if (fast->next != NULL) {
                fast = fast->next->next;

            } else {
                break; 
            }
        }

        previous->next = NULL;  // a组的最后一个节点的Next 为NULL;

        *a = head;
        *b = slow;
    }

}


/**
 * @breif 归并操作,并合并两组
 *
 * 将单链表在中间分成a b两组, 归并操作并合并后的链表:sortList
 *
 * 归并操作:
 *
 *  对比两个分组,将较小的先放入sortList
 *  将第一组剩余的放入sortList
 *  将第二组剩余的放入sortList
 *
 * @param [in]  a组 b组
 * @retval 返回合并后的链
 */
LinkList_t *mergeLinkList(LinkList_t *a, LinkList_t *b) {
    LinkList_t *sortList = NULL, *sortListHead = NULL;

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

    } else if (a != NULL && b == NULL) {
        return a; 

    } else  if (a == NULL && b == NULL){
        return NULL; 
    }

    // 准备并备份排序好的链表头 
    if ( a->value < b->value ) {
        sortListHead = a;
        a = a->next;

    } else {
        sortListHead = b; 
        b = b->next;
    }

    sortList = sortListHead ;

    // 对比两组,将较小的先插入到sortList尾部
    while (a != NULL &&  b!= NULL) {
        if ( a->value < b->value ) {
            sortList->next = a; 
            a = a->next;

        } else {
            sortList->next = b; 
            b = b->next;
        }

        sortList = sortList->next;
    }

    // 将a组中剩余部分插入到sortList尾部
    while (a != NULL) {
        sortList->next = a;

        a = a->next;
        sortList = sortList->next;
    } 

    // 将b组中剩余部分插入到sortList尾部
    while (b != NULL) {
        sortList->next = b;

        b = b->next;
        sortList = sortList->next;
    } 

    return sortListHead;
}


/**
 * @breif 递归二分链表并排序
 * @param [in]  head 单链头 
 * @retval 返回排序后的链
 */
LinkList_t *sortLinkList(LinkList_t *head) {
    LinkList_t *leftLinkList, *rightLinkList;

    if (head == NULL) {
        return NULL; 

    } else if (head->next == NULL) {
        return head; 
    }
   
    getMiddle(head, &leftLinkList, &rightLinkList);

    leftLinkList = sortLinkList(leftLinkList);
    rightLinkList = sortLinkList(rightLinkList);

    return mergeLinkList(leftLinkList, rightLinkList);
}


/**
 * @breif 建立一个简单的单链表,用于测试排序
 * @retval 返回单链表
 */
LinkList_t *createLinkList() {
    int i;
    LinkList_t *head = NULL, *current = NULL, *previous = NULL;

    // 10 个元素,从大到小
    for (i = 10; i > 0; i--) {
        current = (LinkList_t*)malloc(sizeof(LinkList_t)); 

        if (current == NULL) {
            perror("Error: malloc \n"); 
        }

        current->value = i;
        current->next = NULL;
        
        if (head == NULL) {
            head = current; 
            previous = current;

        } else {
            previous->next = current;
            previous = current;
        }
    }

    return head;
}


int main(){
    LinkList_t *head = NULL;

    head = createLinkList();
    head = sortLinkList(head);
   
    while (head != NULL) {
        printf("%d\n", head->value); 
        head = head->next;
    }

    exit(0);
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics