左倾堆的C语言实现

左倾堆(leftist tree 或 leftist heap)又称为左式堆,左偏树,左偏堆,最左堆等。左倾堆是优先队列的一种实现方式,当优先队列需要解决“两个优先队列合并”的问题时,二叉堆的效率就比较差了,这时左倾堆等方式可以很好的解决这类问题。左倾堆合并时间复杂度为O(lgn)。左倾堆在统计问题,最值问题,模拟问题和贪心问题等问题中有广泛的应用。 左倾堆是二叉树,另外还有两个属性,键值和零距离。

键值用来比较节点的大小
零距离(NPL)是 null path length 的缩写,指的是从该结点到达一个没有两个孩子的结点(一个或无孩子)的最短距离,叶节点的NPL为0, NULL的NPL为-1。

左倾堆的性质

  1. 节点的优先级大于或等于它子节点的优先级
  2. 任意节点左孩子的NPL >= 右孩子的NPL
  3. 节点的NPL = 它的右孩子的NPL + 1

左倾堆的定义

typedef struct leftist_heap_s *leftist_heap_pt;

typedef struct leftist_heap_s {  
    leftist_heap_pt left;
    leftist_heap_pt right;  
    Item data;
    int  npl;
} leftist_heap_t;

合并操作图解

合并操作是左倾堆的重点。合并两个左倾堆的过程如下:

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

代码实现如下

leftist_heap_pt 
leftist_heap_merge(leftist_heap_pt h1, leftist_heap_pt h2)
{
    leftist_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 = leftist_heap_merge(h1->right, h2);
    if (h1->left == NULL || h1->right->npl > h1->left->npl) {
        tmp = h1->left;
        h1->left = h1->right;
        h1->right = tmp;
    }

    if (h1->right == NULL || h1->left == NULL) {
        h1->npl = 0;
    }
    else {
        h1->npl = h1->right->npl + 1;
    }
    return h1;
}

流程如下图所示

图一

图二

图三

完整源码

/* leftist_heap.h */
#ifndef LEFTIST_HEAP_H
#define LEFTIST_HEAP_H

typedef int Item;

typedef struct leftist_heap_s *leftist_heap_pt;

typedef struct leftist_heap_s {  
    leftist_heap_pt left;
    leftist_heap_pt right;  
    Item data;
    int  npl;
} leftist_heap_t;


leftist_heap_pt leftist_heap_merge(leftist_heap_pt h1, leftist_heap_pt h2);
leftist_heap_pt leftist_heap_push(leftist_heap_pt heap, Item element);
leftist_heap_pt leftist_heap_pop(leftist_heap_pt heap);
Item leftist_heap_top(leftist_heap_pt heap);
void leftist_heap_destroy(leftist_heap_pt heap);
void leftist_heap_print(leftist_heap_pt heap);
void leftist_heap_export_dot(leftist_heap_pt heap, char *filename, char *label);

#endif

leftist_heap.h

/* leftist_heap.c */
/*
 * =====================================================================================
 *
 *       Filename:  leftist_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 "leftist_heap.h"

static void 
swap(leftist_heap_pt p1, leftist_heap_pt p2)
{
    leftist_heap_t temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

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

leftist_heap_pt 
leftist_heap_merge(leftist_heap_pt h1, leftist_heap_pt h2)
{
    leftist_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 = leftist_heap_merge(h1->right, h2);
    if (h1->left == NULL || h1->right->npl > h1->left->npl) {
        tmp = h1->left;
        h1->left = h1->right;
        h1->right = tmp;
    }

    if (h1->right == NULL || h1->left == NULL) {
        h1->npl = 0;
    }
    else {
        h1->npl = h1->right->npl + 1;
    }
    return h1;
}

leftist_heap_pt 
leftist_heap_push(leftist_heap_pt heap, Item element)
{
    leftist_heap_pt node_pt;
    node_pt = (leftist_heap_pt)malloc(sizeof(leftist_heap_t));
    if (node_pt == NULL) {
        return heap;
    }
    node_pt->left = NULL;
    node_pt->right = NULL;
    node_pt->npl = 0;
    node_pt->data = element;
    return leftist_heap_merge(heap, node_pt);
}

leftist_heap_pt
leftist_heap_pop(leftist_heap_pt heap)
{
    if (heap == NULL) {
        return heap;
    }
    leftist_heap_pt node_left_pt = heap->left;
    leftist_heap_pt node_right_pt = heap->right;
    free(heap);
    return leftist_heap_merge(node_left_pt, node_right_pt);
}

Item 
leftist_heap_top(leftist_heap_pt heap)
{
    return heap->data;
}

void 
leftist_heap_destroy(leftist_heap_pt heap)
{
    if (heap == NULL) {
        return;
    }

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

    free(heap);
}

static void 
leftist_heap_printnode(leftist_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 
leftist_heap_show(leftist_heap_pt heap, int h)
{
    if (heap == NULL) {
        leftist_heap_printnode(heap, h);
        return;
    }
    leftist_heap_show(heap->left, h+1);
    leftist_heap_printnode(heap, h);
    leftist_heap_show(heap->right, h+1);
}

void 
leftist_heap_print(leftist_heap_pt heap)
{
    leftist_heap_show(heap, 0);
}

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

static void 
export_dot(FILE *fp, leftist_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, xlabel=%d];\n", node_name, heap->data, heap->npl);
    }
    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 
leftist_heap_export_dot(leftist_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);
}

leftist_heap.c

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

#include "leftist_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);
    leftist_heap_pt ha = NULL, hb = NULL, hc = NULL;

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


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

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

    leftist_heap_export_dot(hc, "leftist1.dot", "合并结果图");

    leftist_heap_destroy(hc);
    return 0;
}

leftist_heap_test.c

测试程序的运行结果

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

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

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

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

图

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


Date 2016-09-24(星期六) 13:47 By Ming In 算法 Tags 算法,