斜堆的C语言实现

斜堆(skew heap)是左倾堆的一个变种,与左倾堆一样,合并的时间复杂度也是O(lgn)。相对于左倾堆来说,斜堆没有零距离这个概念,而是在合并操作的时候,每次交换左右子树。

斜堆的定义

typedef struct skew_heap_s *skew_heap_pt;

typedef struct skew_heap_s {  
    skew_heap_pt left;
    skew_heap_pt right;  
    Item data;
} skew_heap_t;

合并操作图解

与左倾堆相同,合并操作也是斜堆的重点。合并两个斜堆的过程如下:

  1. 如果一个空斜堆与非空斜堆合并,返回非空斜堆
  2. 如果两个斜都非空,比较两个根节点,取优先级高的根节点为新的根节点。将“优先级较高堆的根节点的右孩子”和“优先级底的堆”进行合并
  3. 合并后,交换新堆根节点的左孩子和右孩子

代码实现如下

skew_heap_pt 
skew_heap_merge(skew_heap_pt h1, skew_heap_pt h2)
{
    skew_heap_pt tmp;
    if (h1 == NULL) {
        return h2;
    }
    if (h2 == NULL) {
        return h1;
    }

    /* 保证h1的优先级大于h2 */
    if (item_cmp(h1->data, h2->data)) {
        swap(h1, h2);
    }

    h1->right = skew_heap_merge(h1->right, h2);
    tmp = h1->left;
    h1->left = h1->right;
    h1->right = tmp;

    return h1;
}

流程如下图所示

图一

图二

图三

图四

图五

图六

图七

图八

图九

完整源码

/* skew_heap.h */
#ifndef SKEW_HEAP_H
#define SKEW_HEAP_H

typedef int Item;

typedef struct skew_heap_s *skew_heap_pt;

typedef struct skew_heap_s {  
    skew_heap_pt left;
    skew_heap_pt right;  
    Item data;
} skew_heap_t;

skew_heap_pt skew_heap_merge(skew_heap_pt h1, skew_heap_pt h2);
skew_heap_pt skew_heap_push(skew_heap_pt heap, Item element);
skew_heap_pt skew_heap_pop(skew_heap_pt heap);
Item skew_heap_top(skew_heap_pt heap);
void skew_heap_destroy(skew_heap_pt heap);
void skew_heap_print(skew_heap_pt heap);
void skew_heap_export_dot(skew_heap_pt heap, char *filename, char *label);

#endif

skew_heap.h

/* skew_heap.c */
/*
 * =====================================================================================
 *
 *       Filename:  skew_heap.c
 *
 *    Description:  斜堆实现
 *
 *        Version:  1.0
 *        Created:  2016-09-25 12:09
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  simon
 *
 * =====================================================================================
 */

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

#include "skew_heap.h"

static void 
swap(skew_heap_pt p1, skew_heap_pt p2)
{
    skew_heap_t temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

/* 
 * 最小堆 ((a) > (b))
 * 最大堆 ({a} < (b))
 * 
 */
#define item_cmp(a, b) ((a) > (b)) /* 最小堆 */

skew_heap_pt 
skew_heap_merge(skew_heap_pt h1, skew_heap_pt h2)
{
    skew_heap_pt tmp;
    if (h1 == NULL) {
        return h2;
    }
    if (h2 == NULL) {
        return h1;
    }

    /* 保证h1的优先级大于h2 */
    if (item_cmp(h1->data, h2->data)) {
        swap(h1, h2);
    }

    h1->right = skew_heap_merge(h1->right, h2);
    tmp = h1->left;
    h1->left = h1->right;
    h1->right = tmp;

    return h1;
}

skew_heap_pt 
skew_heap_push(skew_heap_pt heap, Item element)
{
    skew_heap_pt node_pt;
    node_pt = (skew_heap_pt)malloc(sizeof(skew_heap_t));
    if (node_pt == NULL) {
        return heap;
    }
    node_pt->left = NULL;
    node_pt->right = NULL;
    node_pt->data = element;
    return skew_heap_merge(heap, node_pt);
}

skew_heap_pt
skew_heap_pop(skew_heap_pt heap)
{
    if (heap == NULL) {
        return heap;
    }
    skew_heap_pt node_left_pt = heap->left;
    skew_heap_pt node_right_pt = heap->right;
    free(heap);
    return skew_heap_merge(node_left_pt, node_right_pt);
}

Item 
skew_heap_top(skew_heap_pt heap)
{
    return heap->data;
}

void 
skew_heap_destroy(skew_heap_pt heap)
{
    if (heap == NULL) {
        return;
    }

    skew_heap_destroy(heap->left);
    skew_heap_destroy(heap->right);

    free(heap);
}

static void 
skew_heap_printnode(skew_heap_pt node, int h)
{
    int i;
    for (i=0; i < h; i++) {
        printf("    ");
    }
    if (node == NULL) {
        printf("*\n");
    }
    else {
        printf("%d\n", node->data);
    }
}

static void 
skew_heap_show(skew_heap_pt heap, int h)
{
    if (heap == NULL) {
        skew_heap_printnode(heap, h);
        return;
    }
    skew_heap_show(heap->left, h+1);
    skew_heap_printnode(heap, h);
    skew_heap_show(heap->right, h+1);
}

void 
skew_heap_print(skew_heap_pt heap)
{
    skew_heap_show(heap, 0);
}

/* 打印节点计数变量 */
static int node_num = 0;

static void 
export_dot(FILE *fp, skew_heap_pt heap, char *last_label)
{
    /* 打印节点 */
    char node_name[10] = {0};
    sprintf(node_name, "n%d", node_num);
    if (heap == NULL) {
        fprintf(fp, "%s[style=invis];\n", node_name);
    }
    else {
        fprintf(fp, "%s[label=%d];\n", node_name, heap->data);
    }
    node_num++;

    /* 打印边 */
    if (last_label != NULL) {
        if (heap == NULL) {
            fprintf(fp, "%s->%s[style=invis];\n", last_label, node_name);
        }
        else {
            fprintf(fp, "%s->%s;\n", last_label, node_name);
        }
    }

    if (heap == NULL) {
        return;
    }

    /* 打印子节点 */
    if (heap->left == NULL && heap->right == NULL) {
        return;
    }
    export_dot(fp, heap->left, node_name);
    export_dot(fp, NULL, node_name);
    export_dot(fp, heap->right, node_name);
}

void 
skew_heap_export_dot(skew_heap_pt heap, char *filename, char *label)
{
    FILE *fp;
    fp = fopen(filename, "w");
    fprintf(fp, "digraph g{\nnode[shape=circle];\nlabel=\"%s\";\nlabeljust=l;\nlabelloc=t;\n", label);
    export_dot(fp, heap, NULL);
    fprintf(fp, "}\n");
    fclose(fp);
}

skew_heap.c

/* skew_heap_test.c */
#include <stdlib.h>
#include <stdio.h>

#include "skew_heap.h"

#define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) )

int
main(int argc, char *argv[])
{
    int a[]= {10,40,24,30,36,20,12,16};
    int b[]= {17,13,11,15,19,21,23};
    int i;
    int alen=LENGTH(a);
    int blen=LENGTH(b);
    skew_heap_pt ha = NULL, hb = NULL, hc = NULL;

    printf("== 斜堆(ha)中依次添加: ");
    for(i=0; i<alen; i++)
    {
        printf("%d ", a[i]);
        ha = skew_heap_push(ha, a[i]);
    }
    printf("\n== 斜堆(ha)的详细信息: \n");
    skew_heap_print(ha);


    printf("\n== 斜堆(hb)中依次添加: ");
    for(i=0; i<blen; i++)
    {
        printf("%d ", b[i]);
        hb = skew_heap_push(hb, b[i]);
    }
    printf("\n== 斜堆(hb)的详细信息: \n");
    skew_heap_print(hb);

    hc = skew_heap_merge(ha, hb);
    printf("\n== 合并ha和hb后的详细信息: \n");
    skew_heap_print(hc);

    skew_heap_export_dot(hc, "skew1.dot", "合并结果图");

    skew_heap_destroy(hc);
    return 0;
}

skew_heap_test.c

测试程序的运行结果

== 斜堆(ha)中依次添加: 10 40 24 30 36 20 12 16 
== 斜堆(ha)的详细信息: 
                    *
                40
                    *
            30
                *
        20
            *
    16
        *
10
                *
            36
                *
        24
            *
    12
        *

== 斜堆(hb)中依次添加: 17 13 11 15 19 21 23 
== 斜堆(hb)的详细信息: 
                *
            23
                *
        17
            *
    13
            *
        19
            *
11
            *
        21
            *
    15
        *

== 合并ha和hb后的详细信息: 
                    *
                21
                    *
            15
                *
        12
                    *
                36
                    *
            24
                *
    11
                    *
                23
                    *
            17
                *
        13
                *
            19
                *
10
                    *
                40
                    *
            30
                *
        20
            *
    16
        *

最后生成dot标记文件,然后使用工具可以生成下图

图

转载文章请注明出处: http://mingnote.com


Date 2016-09-25(星期日) 21:16 By Ming In 算法 Tags 算法,