算法--归并排序(链表)

归并排序

http://blog.csdn.net/morewindows/article/details/6678165

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

归并操作:

http://www.tuicool.com/articles/iy2QRn6

归并操作

归并操作(merge),也叫归并算法,指的是将两个顺序序列合并成一个顺序序列的方法。

如:设有数列 [6,202,100,301,38,8,1]

初始状态:6, 202, 100, 301, 38, 8, 1

第一次归并后:[6, 202], [100, 301], [8, 38], [1],比较次数:3;

第二次归并后:[6, 100, 202, 301],[1, 8, 38],比较次数:4;

第三次归并后:[1, 6, 8, 38, 100, 202, 301],比较次数:4;

归并排序  是在归并的操作基础上采用分治法的排序方法; (归并 + 分治)
归并操作  就是将两个有序的子列合并成一个有序总列,
分治法  就是通过二分法将序列不断分成子列。

个人理解

与快速排序方法相同的是, 两者都采用分治方法,  即将一个大规模的问题, 分解成两个小规模的问题, 然后对每个小规模问题, 做递归运算。

与快速排序不同的是,  快速排序只有分治过程, 分治过程中就行行筛选(实现部分排序),

归并排序, 分治过程, 就是很单纯, 只有分的动作, 然后归并排序, 有一个合并的过程, 合并过程执行了排序工功能。

这两种方式是两种截然不同的整理杂物的模式:

1、 快速排序, 精髓体现在筛选, 一分二, 一大 一小。

例如杂物太多了,不好按个的大小排序处理, 先初步按照大小初步分分类。

2、 归并排序, 精髓体现在合并, 物品按照相邻位置, 两个元素为一组, 两个元素合并为一个。

杂物不多, 两两归一, 成为有序的一个列。然后列列合并为一个更大的有序列。

归并排序, 既可以使用递归方式实现, 也可以采用堆栈工具实现。

递归方式比较容易理解, 示意代码:

void mergesort(int a[], int first, int last, int temp[])
{
    if (first < last)
    {
        int mid = (first + last) / 2;
        mergesort(a, first, mid, temp);    //左边有序
        mergesort(a, mid + 1, last, temp); //右边有序
        mergearray(a, first, mid, last, temp); //再将二个有序数列合并
    }
}  

堆栈方法可以克服递归方法带来的缺点, 递归方法由于深层的递归调用, 会耗费大量内存,如果带合并数目巨大,则可能耗费完栈的资源。

同时递归方法,有利于数组形式写法, 数组写法的代码参考(http://blog.csdn.net/morewindows/article/details/6678165),

堆栈方法,更加适合链表, 因为递归方法, 是按照数组元素的下标(数组的索引)分治 和 归并的, 如果对于链表实现, 则需要二外的链表节点位置计算的开销。

C程序实现

链表方式实现 此算法(merge sort)参考:

https://github.com/fanqingsong/code-snippet/blob/master/C/MergeSort/mergesortList.c

归并操作:

PT_LIST_LINKNODE Merge_TwoList(PT_LIST_LINKNODE ptLinkNode_one, PT_LIST_LINKNODE ptLinkNode_two)
{
    PT_NODE_LISTHEAD ptNodeListHead_one = NULL;
    PT_NODE_LISTHEAD ptNodeListHead_two = NULL;
    PT_NODE_LISTHEAD ptNodeListHead_merged = NULL;

    PT_LIST_LINKNODE ptListHead_One = NULL;
    PT_LIST_LINKNODE ptListHead_Two = NULL;
    PT_LIST_LINKNODE ptListHead_merged = NULL;

    PT_NODE ptNodeOne = NULL;
    PT_NODE ptNodeTwo = NULL;

    E_BOOL_TYPE bIsListOneGo = TRUE;
    E_BOOL_TYPE bIsListTwoGo = TRUE;

    PT_LIST_LINKNODE ptLinkNode_merged = NULL;

    ptNodeListHead_merged = GetNode_ListHead();
    if ( !ptNodeListHead_merged )
    {
        return NULL;
    }
    ptListHead_merged = &(ptNodeListHead_merged->tListHead);

    ptNodeListHead_one = list_entry(ptLinkNode_one, T_NODE_LISTHEAD, tLinkNode);
    ptNodeListHead_two = list_entry(ptLinkNode_two, T_NODE_LISTHEAD, tLinkNode);

    ptListHead_One = &(ptNodeListHead_one->tListHead);
    ptListHead_Two = &(ptNodeListHead_two->tListHead);

    // merge list one and list two into a new list
    ptNodeOne = List_DetachFirstNode(ptListHead_One);
    ptNodeTwo = List_DetachFirstNode(ptListHead_Two);
    while( ptNodeOne && ptNodeTwo )
    {
        // node one is smaller
        if ( strcmp(ptNodeOne->str, ptNodeTwo->str) < 0 )
        {
            List_AddNode2Tail(ptListHead_merged, ptNodeOne);

            ptNodeOne = NULL;

            // list one shall get next node, list two keep current node
            bIsListOneGo = TRUE;
            bIsListTwoGo = FALSE;
        }
        // node two is smaller or equal
        else
        {
            List_AddNode2Tail(ptListHead_merged, ptNodeTwo);

            ptNodeTwo = NULL;

            // list one shall keep current node, list two shall get next node
            bIsListOneGo = FALSE;
            bIsListTwoGo = TRUE;
        }

        if ( bIsListOneGo )
        {
            ptNodeOne = List_DetachFirstNode(ptListHead_One);
        }

        if ( bIsListTwoGo )
        {
            ptNodeTwo = List_DetachFirstNode(ptListHead_Two);
        }
    }

    if ( ptNodeOne )
    {
        List_AddNode2Tail(ptListHead_merged, ptNodeOne);
        ptNodeOne = NULL;
    }

    if ( ptNodeTwo )
    {
        List_AddNode2Tail(ptListHead_merged, ptNodeTwo);
        ptNodeTwo = NULL;
    }

    // if list one has node yet, add them to merge list
    ptNodeOne = List_DetachFirstNode(ptListHead_One);
    while( ptNodeOne )
    {
        List_AddNode2Tail(ptListHead_merged, ptNodeOne);

        ptNodeOne = List_DetachFirstNode(ptListHead_One);
    }

    // if list two has node yet, add them to merge list
    ptNodeTwo = List_DetachFirstNode(ptListHead_Two);
    while( ptNodeTwo )
    {
        List_AddNode2Tail(ptListHead_merged, ptNodeTwo);

        ptNodeTwo = List_DetachFirstNode(ptListHead_Two);
    }

    FreeNode_ListHead(&ptNodeListHead_one);
    FreeNode_ListHead(&ptNodeListHead_two);

    ptLinkNode_merged = &(ptNodeListHead_merged->tLinkNode);

    return ptLinkNode_merged;
}

对于链表的一趟归并:

// execute one merge process, from left stack to right stack
void Merge_OneTime(PT_LIST_LINKNODE ptStackSrc, PT_LIST_LINKNODE ptStackDest)
{
    // execute two way merge
    PT_LIST_LINKNODE ptLinkNode_one = NULL;
    PT_LIST_LINKNODE ptLinkNode_two = NULL;
    PT_LIST_LINKNODE ptLinkNode_merged = NULL;

    while( !IsStackEmpty(ptStackSrc) )
    {
        ptLinkNode_one = PopLinkNodeFromStack(ptStackSrc);
        ptLinkNode_two = PopLinkNodeFromStack(ptStackSrc);

        // src stack has only one list, add to destine stack
        if ( !ptLinkNode_two )
        {
            PushLinkNodeOnStack(ptStackDest, ptLinkNode_one);
            break;
        }

        //src stack has two list yet, merge it, add merged list to destine stack
        ptLinkNode_merged = Merge_TwoList(ptLinkNode_one, ptLinkNode_two);

        PushLinkNodeOnStack(ptStackDest, ptLinkNode_merged);
    }
}

归并排序core接口

实现思路:

1、将list中每一个元素都转变为一个list, 然后压入 leftStack

LIST: (1)->(2)->(3)

leftStack: (LIST:(1)) -> (LIST:(2))->(LIST:(3))

2、 将leftStack中从栈顶开始, 每两个元素(list)一组, 进行合并, 合并之后的list,压入rightStack

rightStack: (LIST:(1)) -> (LIST:(2)->(3))

3、 按照2规则, 将rightStack中链表,归并后, 压入 leftStack。 循环执行 2 3 ,直到 栈中只有一个链表停止。 此链表即为排序完成的链表。

leftStack: (LIST:(1)->(2)->(3))

void List_MergeSort(PT_LIST_LINKNODE ptListHead)
{
    // left stack for the even times of merging
    T_LIST_LINKNODE tStackLeft = {NULL, NULL};
    PT_LIST_LINKNODE ptStackLeft = &tStackLeft;

    // right stack for the odd times of merging
    T_LIST_LINKNODE tStackRight = {NULL, NULL};
    PT_LIST_LINKNODE ptStackRight = &tStackRight;

    // final list may be in left stack or right stack, the final stack pointer is the result
    PT_LIST_LINKNODE ptStackFinal = NULL;

    PT_LIST_LINKNODE ptLinkNode_merged = NULL;
    PT_NODE_LISTHEAD ptNodeListHead_merged = NULL;
    PT_LIST_LINKNODE ptListHead_merged = NULL;

    INIT_LIST_HEAD(&tStackLeft);
    INIT_LIST_HEAD(&tStackRight);

    if ( IsListEmpty(ptListHead) )
    {
        return ;
    }

    MakeEveryNodeList2Stack(ptListHead, ptStackLeft);
    //PrintStackLists(ptStackLeft);

    while ( TRUE )
    {
        // merge the lists from left stack into right stack
        Merge_OneTime(ptStackLeft, ptStackRight);
        //PrintStackLists(ptStackLeft);
        //PrintStackLists(ptStackRight);
        if ( HasStackOneList(ptStackRight) )
        {
            ptStackFinal = ptStackRight;
            break;
        }

        // merge the lists from right stack into left stack
        Merge_OneTime(ptStackRight, ptStackLeft);
        //PrintStackLists(ptStackLeft);
        //PrintStackLists(ptStackRight);
        if ( HasStackOneList(ptStackLeft) )
        {
            ptStackFinal = ptStackLeft;
            break;
        }
    }

    // record merged final list into list head
    ptLinkNode_merged = PopLinkNodeFromStack(ptStackFinal);
    ptNodeListHead_merged = list_entry(ptLinkNode_merged, T_NODE_LISTHEAD, tLinkNode);
    ptListHead_merged = &(ptNodeListHead_merged->tListHead);

    List_ReplaceHead(ptListHead_merged, ptListHead);
    FreeNode_ListHead(&ptNodeListHead_merged);
}

将表中节点全部转换为 子链表函数:

// make every node be a list and push the stack on stack
void MakeEveryNodeList2Stack(PT_LIST_LINKNODE ptListHead, PT_LIST_LINKNODE ptStack)
{
    PT_LIST_LINKNODE ptLinkNode = NULL;
    PT_LIST_LINKNODE ptNextCache = NULL;
    PT_NODE_LISTHEAD ptNodeListHead = NULL;

    // make all elements enter the left stack
    list_for_each_safe(ptListHead, ptLinkNode, ptNextCache)
    {
        // detatch this node from list
        list_del(ptLinkNode);

        ptNodeListHead = GetNode_ListHead();
        List_AddLink2Tail(&(ptNodeListHead->tListHead), ptLinkNode);

        // push new list on left stack
        PushLinkNodeOnStack(ptStack, &(ptNodeListHead->tLinkNode));
    }
}

链表操作改进

由于宏定义linux api容易引起宏使用的混淆问题, 同时这些宏都是一些 短小语句的定义, 按照linux系统链表的定义方法进行参考,

给出链表操作接口使用 inline 函数方式:

// initialize the head and tail of list head as self
static inline void INIT_LIST_HEAD(PT_LIST_LINKNODE ptListHead)
{
    ptListHead->next = ptListHead;
    ptListHead->prev = ptListHead;
}

// insert new link node between previous link node and next link node
static inline void _list_add(PT_LIST_LINKNODE ptNewLink,
                  PT_LIST_LINKNODE ptPrevLink,
                  PT_LIST_LINKNODE ptNextLink)
{
    // splice new link and next link
    ptNewLink->next = ptNextLink;
    ptNextLink->prev = ptNewLink;

    // splice new link and previous link
    ptNewLink->prev = ptPrevLink;
    ptPrevLink->next = ptNewLink;
}

// delete the specific link node from list
static inline void list_del(PT_LIST_LINKNODE ptLinkNode)
{
    ptLinkNode->prev->next = ptLinkNode->next;
    ptLinkNode->next->prev = ptLinkNode->prev;

    ptLinkNode->prev = NULL;
    ptLinkNode->next = NULL;
}

// add new list node to  list head
static inline void list_add_head(PT_LIST_LINKNODE ptListHead,  PT_LIST_LINKNODE ptListNewLink)
{
    PT_LIST_LINKNODE ptPrevLink = ptListHead;
    PT_LIST_LINKNODE ptNextLink = ptListHead->next;

    _list_add(ptListNewLink,  ptPrevLink, ptNextLink);
}

// add new list node to  list tail
static inline void list_add_tail(PT_LIST_LINKNODE ptListHead, PT_LIST_LINKNODE ptListNewLink)
{
    PT_LIST_LINKNODE ptPrevLink = ptListHead->prev;
    PT_LIST_LINKNODE ptNextLink = ptListHead;

    _list_add(ptListNewLink, ptPrevLink, ptNextLink);
}
时间: 2024-10-13 22:15:33

算法--归并排序(链表)的相关文章

leecode 归并排序 链表(java)

写了好久,终于写成了.第一次zai leecode错题,题目质量很高,适合面试,与 1.归并排序是稳定的,在java中 Arrays.sort(a);中对于对象的排序就是归并排序.对于原子类型数据使用的是快排. 2.算法复杂度,我们都知道归并排序的最好最坏最差复杂度为nlogn,空间复杂度为n,在链表当中,空间复杂度j降为O(1). 3.写链表的排序 1.分: 使用书上的快慢指针来获得中间节点,分割成2个链表 2.和: 将两个链表合成一个,比较简单 3. 主程序 ListNode lmerge(

Java实现算法归并排序(MergeSort)

归并排序 归并排序 (merge sort) 是一类与插入排序.交换排序.选择排序不同的另一种排序方法.归并的含义是将两个或两个以上的有序表合并成一个新的有序表.归并排序有多路归并排序.两路归并排序 , 可用于内排序,也可以用于外排序.这里仅对内排序的两路归并方法进行讨论. 1.两路归并排序算法思路 ①把 n 个记录看成 n 个长度为1的有序子表: ②进行两两归并使记录关键字有序,得到 n/2 个长度为 2 的有序子表: ③重复第②步直到所有记录归并成一个长度为 n 的有序表为止. 实例: 2.

图解堆算法、链表、栈与队列(Mark)

原文地址: 图解堆算法.链表.栈与队列(多图预警) 堆(heap),是一类特殊的数据结构的统称.它通常被看作一棵树的数组对象.在队列中,调度程序反复提取队列中的第一个作业并运行,因为实际情况中某些时间较短的任务却可能需要等待很长时间才能开始执行,或者某些不短小.但很重要的作业,同样应当拥有优先权.而堆就是为了解决此类问题而设计的数据结构.--

算法学习 - 链表的游标实现~ C++

链表的游标实现,就是用另外一种方法来访问链表,模拟游标. 在我学习的理解中,就是创建一个节点数组,模拟内存的排列,然后从其中来申请内存和释放内存.但是实际的内存没有被释放~ 下面直接贴代码了: // // main.cpp // CursorList // // Created by Alps on 14-7-27. // Copyright (c) 2014年 chen. All rights reserved. // #include <iostream> #define CursorSp

排序算法——归并排序

归并排序是分治法的典型举例. 分治法的思想是,将原有问题分解为几个规模较小但类似于原问题的子问题,递归的求解这些子问题,然后再合并这些子问题的解来建立原问题的解. 分治模式在每层递归时都有三个步骤: 分解原问题为若干子问题,这些子问题是原问题的规模较小的实例. 解决这些子问题,递归地求解各子问题.然而,若子问题的规模足够小,则直接求解. 合并这些子问题的解成原问题的解. 归并排序算法完全遵循分治模式.直观上其操作如下: 分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列. 解决:使用

Java数据结构和算法之链表

三.链表 链结点 在链表中,每个数据项都被包含在'点"中,一个点是某个类的对象,这个类可认叫做LINK.因为一个链表中有许多类似的链结点,所以有必要用一个不同于链表的类来表达链结点.每个LINK对象中都包含一个对下一个点引用的字段(通常叫做next)但是本身的对象中有一个字段指向对第一个链结点的引用. 单链表 用一组地址任意的存储单元存放线性表中的数据元素. 以元素(数据元素的映象)  + 指针(指示后继元素存储位置)  = 结点(表示数据元素 或 数据元素的映象) 以"结点的序列&q

经典排序算法 - 归并排序Merge sort

经典排序算法 - 归并排序Merge sort 原理,把原始数组分成若干子数组,对每个子数组进行排序, 继续把子数组与子数组合并,合并后仍然有序,直到所有合并完,形成有序的数组 举例 无序数组[6 2 4 1 5 9] 先看一下每一个步骤下的状态,完了再看合并细节 第一步 [6 2 4 1 5 9]原始状态 第二步 [2 6] [1 4] [5 9]两两合并排序,排序细节后边介绍 第三步 [1 2 4 6] [5 9]继续两组两组合并 第四步 [1 2 4 5 6 9]合并完成,排序完成 输出结

算法:链表

通过链表的一些题目,了解链表的基本操作实现,掌握递归算法的基本思路,掌握扎实的编程习惯. 一.单链表基本操作 1.1.单链表节点定义 struct ListNode { int value; ListNode *pNext; }; 1.2.在尾部插入节点 void AddToTail(ListNode *pHead, int value) { /* * 尾部插入 * 添加空的头结点,简化代码 */ ListNode *node = new ListNode(); node->value = va

C++算法之链表排序的代码

下面的资料是关于C++算法之链表排序的代码. { return; while(curr){ prev = curr; curr = curr->next; insert_for_sort_operation(ppNode, prev); } return; } b)对于待插入的节点,选择合适的位置插入即可 { return; } while(cur){ if(pNode->data < cur->data) break; prev = cur; cur = cur->next