zoukankan      html  css  js  c++  java
  • 斜堆(一)之 C语言的实现

    概要

    本章介绍斜堆。和以往一样,本文会先对斜堆的理论知识进行简单介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现;实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可。若文章有错误或不足的地方,请不吝指出!

    目录
    1. 斜堆的介绍
    2. 斜堆的基本操作
    3. 斜堆的C实现(完整源码)
    4. 斜堆的C测试程序

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3638493.html


    更多内容:数据结构与算法系列 目录

    斜堆的介绍

    斜堆(Skew heap)也叫自适应堆(self-adjusting heap),它是左倾堆的一个变种。和左倾堆一样,它通常也用于实现优先队列。它的合并操作的时间复杂度也是O(lg n)。

    相比于左倾堆,斜堆的节点没有"零距离"这个属性。除此之外,它们斜堆的合并操作也不同。斜堆的合并操作算法如下:
    (01) 如果一个空斜堆与一个非空斜堆合并,返回非空斜堆。
    (02) 如果两个斜堆都非空,那么比较两个根节点,取较小堆的根节点为新的根节点。将"较小堆的根节点的右孩子"和"较大堆"进行合并。
    (03) 合并后,交换新堆根节点的左孩子和右孩子。
            第(03)步是斜堆和左倾堆的合并操作差别的关键所在,如果是左倾堆,则合并后要比较左右孩子的零距离大小,若右孩子的零距离 > 左孩子的零距离,则交换左右孩子;最后,在设置根的零距离。

    斜堆的基本操作

    1. 头文件

    #ifndef _SKEW_HEAP_H_
    #define _SKEW_HEAP_H_
    
    typedef int Type;
    
    typedef struct _SkewNode{
        Type   key;                // 关键字(键值)
        struct _SkewNode *left;    // 左孩子
        struct _SkewNode *right;   // 右孩子
    }SkewNode, *SkewHeap;
    
    // 前序遍历"斜堆"
    void preorder_skewheap(SkewHeap heap);
    // 中序遍历"斜堆"
    void inorder_skewheap(SkewHeap heap);
    // 后序遍历"斜堆"
    void postorder_skewheap(SkewHeap heap);
    
    // 获取最小值(保存到pval中),成功返回0,失败返回-1。
    int skewheap_minimum(SkewHeap heap, int *pval);
    // 合并"斜堆x"和"斜堆y",并返回合并后的新树
    SkewNode* merge_skewheap(SkewHeap x, SkewHeap y);
    // 将结点插入到斜堆中,并返回根节点
    SkewNode* insert_skewheap(SkewHeap heap, Type key);
    // 删除结点(key为节点的值),并返回根节点
    SkewNode* delete_skewheap(SkewHeap heap);
    
    // 销毁斜堆
    void destroy_skewheap(SkewHeap heap);
    
    // 打印斜堆
    void print_skewheap(SkewHeap heap);
    
    #endif

    SkewNode是斜堆对应的节点类。

    2. 合并

    /* 
     * 合并"斜堆x"和"斜堆y"
     *
     * 返回值:
     *     合并得到的树的根节点
     */
    SkewNode* merge_skewheap(SkewHeap x, SkewHeap y)
    {
        if(x == NULL)
            return y;
        if(y == NULL)
            return x;
    
        // 合并x和y时,将x作为合并后的树的根;
        // 这里的操作是保证: x的key < y的key
        if(x->key > y->key)
            swap_skewheap_node(x, y);
    
        // 将x的右孩子和y合并,
        // 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
        SkewNode *tmp = merge_skewheap(x->right, y);
        x->right = x->left;
        x->left  = tmp;
    
        return x;
    }

    merge_skewheap(x, y)的作用是合并x和y这两个斜堆,并返回得到的新堆。merge_skewheap(x, y)是递归实现的。

    3. 添加

    /* 
     * 新建结点(key),并将其插入到斜堆中
     *
     * 参数说明:
     *     heap 斜堆的根结点
     *     key 插入结点的键值
     * 返回值:
     *     根节点
     */
    SkewNode* insert_skewheap(SkewHeap heap, Type key)
    {
        SkewNode *node;    // 新建结点
    
        // 如果新建结点失败,则返回。
        if ((node = (SkewNode *)malloc(sizeof(SkewNode))) == NULL)
            return heap;
        node->key = key;
        node->left = node->right = NULL;
    
        return merge_skewheap(heap, node);
    }

    insert_skewheap(heap, key)的作用是新建键值为key的结点,并将其插入到斜堆中,并返回堆的根节点。

    4. 删除

    /* 
     * 取出根节点
     *
     * 返回值:
     *     取出根节点后的新树的根节点
     */
    SkewNode* delete_skewheap(SkewHeap heap)
    {
        SkewNode *l = heap->left;
        SkewNode *r = heap->right;
    
        // 删除根节点
        free(heap);
    
        return merge_skewheap(l, r); // 返回左右子树合并后的新树
    }

    delete_skewheap(heap)的作用是删除斜堆的最小节点,并返回删除节点后的斜堆根节点。

    注意关于斜堆的"前序遍历"、"中序遍历"、"后序遍历"、"打印"、"销毁"等接口就不再单独介绍了。后文的源码中有给出它们的实现代码,Please RTFSC(Read The Fucking Source Code)!

    斜堆的C实现(完整源码)

    斜堆的头文件(skewheap.h)

     1 #ifndef _SKEW_HEAP_H_
     2 #define _SKEW_HEAP_H_
     3 
     4 typedef int Type;
     5 
     6 typedef struct _SkewNode{
     7     Type   key;                    // 关键字(键值)
     8     struct _SkewNode *left;    // 左孩子
     9     struct _SkewNode *right;    // 右孩子
    10 }SkewNode, *SkewHeap;
    11 
    12 // 前序遍历"斜堆"
    13 void preorder_skewheap(SkewHeap heap);
    14 // 中序遍历"斜堆"
    15 void inorder_skewheap(SkewHeap heap);
    16 // 后序遍历"斜堆"
    17 void postorder_skewheap(SkewHeap heap);
    18 
    19 // 获取最小值(保存到pval中),成功返回0,失败返回-1。
    20 int skewheap_minimum(SkewHeap heap, int *pval);
    21 // 合并"斜堆x"和"斜堆y",并返回合并后的新树
    22 SkewNode* merge_skewheap(SkewHeap x, SkewHeap y);
    23 // 将结点插入到斜堆中,并返回根节点
    24 SkewNode* insert_skewheap(SkewHeap heap, Type key);
    25 // 删除结点(key为节点的值),并返回根节点
    26 SkewNode* delete_skewheap(SkewHeap heap);
    27 
    28 // 销毁斜堆
    29 void destroy_skewheap(SkewHeap heap);
    30 
    31 // 打印斜堆
    32 void print_skewheap(SkewHeap heap);
    33 
    34 #endif
    View Code

    斜堆的实现文件(skewheap.c)

      1 /**
      2  * C语言实现的斜堆
      3  *
      4  * @author skywang
      5  * @date 2014/03/31
      6  */
      7 
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include "skewheap.h"
     11 
     12 /*
     13  * 前序遍历"斜堆"
     14  */
     15 void preorder_skewheap(SkewHeap heap)
     16 {
     17     if(heap != NULL)
     18     {
     19         printf("%d ", heap->key);
     20         preorder_skewheap(heap->left);
     21         preorder_skewheap(heap->right);
     22     }
     23 }
     24 
     25 /*
     26  * 中序遍历"斜堆"
     27  */
     28 void inorder_skewheap(SkewHeap heap)
     29 {
     30     if(heap != NULL)
     31     {
     32         inorder_skewheap(heap->left);
     33         printf("%d ", heap->key);
     34         inorder_skewheap(heap->right);
     35     }
     36 }
     37 
     38 /*
     39  * 后序遍历"斜堆"
     40  */
     41 void postorder_skewheap(SkewHeap heap)
     42 {
     43     if(heap != NULL)
     44     {
     45         postorder_skewheap(heap->left);
     46         postorder_skewheap(heap->right);
     47         printf("%d ", heap->key);
     48     }
     49 }
     50 
     51 /*
     52  * 交换两个节点的内容
     53  */
     54 static void swap_skewheap_node(SkewNode *x, SkewNode *y)
     55 {
     56     SkewNode tmp = *x;
     57     *x = *y;
     58     *y = tmp;
     59 }
     60 
     61 /* 
     62  * 获取最小值
     63  *
     64  * 返回值:
     65  *    成功返回0,失败返回-1
     66  */
     67 int skewheap_minimum(SkewHeap heap, int *pval)
     68 {
     69     if (heap == NULL)
     70         return -1;
     71 
     72     *pval = heap->key;
     73 
     74     return 0;
     75 }
     76  
     77 /* 
     78  * 合并"斜堆x"和"斜堆y"
     79  *
     80  * 返回值:
     81  *     合并得到的树的根节点
     82  */
     83 SkewNode* merge_skewheap(SkewHeap x, SkewHeap y)
     84 {
     85     if(x == NULL)
     86         return y;
     87     if(y == NULL)
     88         return x;
     89 
     90     // 合并x和y时,将x作为合并后的树的根;
     91     // 这里的操作是保证: x的key < y的key
     92     if(x->key > y->key)
     93         swap_skewheap_node(x, y);
     94 
     95     // 将x的右孩子和y合并,
     96     // 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
     97     SkewNode *tmp = merge_skewheap(x->right, y);
     98     x->right = x->left;
     99     x->left  = tmp;
    100 
    101     return x;
    102 }
    103 
    104 /* 
    105  * 新建结点(key),并将其插入到斜堆中
    106  *
    107  * 参数说明:
    108  *     heap 斜堆的根结点
    109  *     key 插入结点的键值
    110  * 返回值:
    111  *     根节点
    112  */
    113 SkewNode* insert_skewheap(SkewHeap heap, Type key)
    114 {
    115     SkewNode *node;    // 新建结点
    116 
    117     // 如果新建结点失败,则返回。
    118     if ((node = (SkewNode *)malloc(sizeof(SkewNode))) == NULL)
    119         return heap;
    120     node->key = key;
    121     node->left = node->right = NULL;
    122 
    123     return merge_skewheap(heap, node);
    124 }
    125 
    126 /* 
    127  * 取出根节点
    128  *
    129  * 返回值:
    130  *     取出根节点后的新树的根节点
    131  */
    132 SkewNode* delete_skewheap(SkewHeap heap)
    133 {
    134     SkewNode *l = heap->left;
    135     SkewNode *r = heap->right;
    136 
    137     // 删除根节点
    138     free(heap);
    139 
    140     return merge_skewheap(l, r); // 返回左右子树合并后的新树
    141 }
    142 
    143 /*
    144  * 销毁斜堆
    145  */
    146 void destroy_skewheap(SkewHeap heap)
    147 {
    148     if (heap==NULL)
    149         return ;
    150 
    151     if (heap->left != NULL)
    152         destroy_skewheap(heap->left);
    153     if (heap->right != NULL)
    154         destroy_skewheap(heap->right);
    155 
    156     free(heap);
    157 }
    158 
    159 /*
    160  * 打印"斜堆"
    161  *
    162  * heap       -- 斜堆的节点
    163  * key        -- 节点的键值 
    164  * direction  --  0,表示该节点是根节点;
    165  *               -1,表示该节点是它的父结点的左孩子;
    166  *                1,表示该节点是它的父结点的右孩子。
    167  */
    168 static void skewheap_print(SkewHeap heap, Type key, int direction)
    169 {
    170     if(heap != NULL)
    171     {
    172         if(direction==0)    // heap是根节点
    173             printf("%2d is root
    ", heap->key);
    174         else                // heap是分支节点
    175             printf("%2d is %2d's %6s child
    ", heap->key, key, direction==1?"right" : "left");
    176 
    177         skewheap_print(heap->left, heap->key, -1);
    178         skewheap_print(heap->right,heap->key,  1);
    179     }
    180 }
    181 
    182 void print_skewheap(SkewHeap heap)
    183 {
    184     if (heap != NULL)
    185         skewheap_print(heap, heap->key,  0);
    186 }
    View Code

    斜堆的测试程序(skewheap_test.c)

     1 /**
     2  * C语言实现的斜堆
     3  *
     4  * @author skywang
     5  * @date 2014/03/31
     6  */
     7 
     8 #include <stdio.h>
     9 #include "skewheap.h"
    10 
    11 #define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) )
    12 
    13 void main()
    14 {
    15     int i;
    16     int a[]= {10,40,24,30,36,20,12,16};
    17     int b[]= {17,13,11,15,19,21,23};
    18     int alen=LENGTH(a);
    19     int blen=LENGTH(b);
    20     SkewHeap ha,hb;
    21 
    22     ha=hb=NULL;
    23 
    24     printf("== 斜堆(ha)中依次添加: ");
    25     for(i=0; i<alen; i++)
    26     {
    27         printf("%d ", a[i]);
    28         ha = insert_skewheap(ha, a[i]);
    29     }
    30     printf("
    == 斜堆(ha)的详细信息: 
    ");
    31     print_skewheap(ha);
    32 
    33 
    34     printf("
    == 斜堆(hb)中依次添加: ");
    35     for(i=0; i<blen; i++)
    36     {
    37         printf("%d ", b[i]);
    38         hb = insert_skewheap(hb, b[i]);
    39     }
    40     printf("
    == 斜堆(hb)的详细信息: 
    ");
    41     print_skewheap(hb);
    42 
    43     // 将"斜堆hb"合并到"斜堆ha"中。
    44     ha = merge_skewheap(ha, hb);
    45     printf("
    == 合并ha和hb后的详细信息: 
    ");
    46     print_skewheap(ha);
    47 
    48 
    49     // 销毁斜堆
    50     destroy_skewheap(ha);
    51 }
    View Code

    斜堆的C测试程序

    斜堆的测试程序已经包含在它的实现文件(skewheap_test.c)中了,这里仅给出它的运行结果:

    == 斜堆(ha)中依次添加: 10 40 24 30 36 20 12 16 
    == 斜堆(ha)的详细信息: 
    10 is root
    16 is 10's   left child
    20 is 16's   left child
    30 is 20's   left child
    40 is 30's   left child
    12 is 10's  right child
    24 is 12's   left child
    36 is 24's   left child
    
    == 斜堆(hb)中依次添加: 17 13 11 15 19 21 23 
    == 斜堆(hb)的详细信息: 
    11 is root
    13 is 11's   left child
    17 is 13's   left child
    23 is 17's   left child
    19 is 13's  right child
    15 is 11's  right child
    21 is 15's   left child
    
    == 合并ha和hb后的详细信息: 
    10 is root
    11 is 10's   left child
    12 is 11's   left child
    15 is 12's   left child
    21 is 15's   left child
    24 is 12's  right child
    36 is 24's   left child
    13 is 11's  right child
    17 is 13's   left child
    23 is 17's   left child
    19 is 13's  right child
    16 is 10's  right child
    20 is 16's   left child
    30 is 20's   left child
    40 is 30's   left child
  • 相关阅读:
    复合表达式
    用DOM4J解析XML文件案例
    XPath可以快速定位到Xml中的节点或者属性。XPath语法很简单,但是强大够用,它也是使用xslt的基础知识。
    java base64编码和解码
    String空格删除和java删除字符串最后一个字符的几种方法
    java解析xml汇总
    XML解析——Java中XML的四种解析方式
    Java 读写Properties配置文件
    Spring + Mybatis 使用 PageHelper 插件分页
    Mybatis分页插件-PageHelper的使用
  • 原文地址:https://www.cnblogs.com/skywang12345/p/3638493.html
Copyright © 2011-2022 走看看