D堆的C语言实现

D堆是二叉堆的简单推广,跟二叉堆的区别是所有的节点都有d个儿子,代码也跟二叉堆的相似。下图表示一个D=3的最小D堆。

图

D堆插入操作的时间改进为O(log(d,N)),然面删除操作要花费较大时间,一般需要O(d*log(d,N))。如果D不是2的幂,也不能通过二进制移位来实现乘法和除法,这样也大大增加运行时间。 大部分时候总是使用二叉堆,但D堆也在一些情况下使用。比如,插入操作多删除操作多很多时,或是优先队列太大不能完全装入内存时。

通常D堆也跟二叉堆一样以数组存储,层为 log(d,n),以下的代码来定义D堆结构。

typedef struct d_heap_s* d_heap_pt;

typedef struct d_heap_s {
    int size;
    int degree;
    int current;
    Item *data;
} d_heap_t;

当以数组存储时,D堆的父子节点下标存在如下关系。

  • 索引为i的孩子的索引是 (i*d + j),其中j = 1, 2, ..., d;
  • 索引为i的父结点的索引是 (i - 1) / d;

添加元素图解

D堆的元素插入步骤跟二叉树相同,在堆中添加元素时需要执行向上堆化操作,假设在最小堆中添加15,需要执行以下步骤。

图一

图二

图三

代码实现如下:

/* 
 *        Name:  fixup
 * Description:  向上堆化
 * 
 */
static void 
fixup(d_heap_pt heap, int k)
{
    int degree = heap->degree;
    while (k>0 && item_cmp(heap->data[(k - 1) / degree],heap->data[k])) {
        swap(heap->data, k, (k - 1) / degree);
        k = (k - 1) / degree;
    }
}

int 
d_heap_push(d_heap_pt heap, Item element)
{
    if (heap == NULL) {
        return 0;
    }
    if (heap->current == heap->size) {
        return 0;
    }
    heap->data[heap->current] = element;
    fixup(heap, heap->current++);
    return 1;
}

删除堆顶元素图解

D堆的元素插入步骤也跟二叉树相同,在堆中移除堆顶元素时需要执行向下堆化操作,假设在最小堆中移除堆顶完素,需要执行以下步骤。

图一

图二

图三

图四

代码实现如下:

/* 
 *        Name:  fixdown
 * Description:  向下堆化
 * 
 */
static void 
fixdown(d_heap_pt heap, int k, int end)
{
    int temp, tail, i;
    int degree = heap->degree;
    while (k*degree + 1 <= end) {
        tail = k*degree + degree;
        tail = tail < end ? tail : end;
        for (i = k*degree + 1, temp = i; i < tail; i++) {
            if (item_cmp(heap->data[temp], heap->data[i])) {
                temp = i;
            }
        }
        if (!item_cmp(heap->data[k], heap->data[temp])) {
            break;
        }
        swap(heap->data, k, temp);
        k = temp;
    }
}

Item 
d_heap_pop(d_heap_pt heap)
{
    swap(heap->data, 0, heap->current - 1);
    fixdown(heap, 0, heap->current - 2);
    return heap->data[--heap->current];
}

完整源码

/* d_heap.h */
#ifndef D_HEAP_H
#define D_HEAP_H

typedef int Item;
typedef struct d_heap_s* d_heap_pt;

typedef struct d_heap_s {
    int size;
    int degree;
    int current;
    Item *data;
} d_heap_t;


d_heap_pt d_heap_init(int size, int degree);
void d_heap_free(d_heap_pt heap);
void d_heap_print(d_heap_pt heap);
int d_heap_push(d_heap_pt heap, Item element);
Item d_heap_pop(d_heap_pt heap);
Item d_heap_top(d_heap_pt heap);
int d_heap_empty(d_heap_pt heap);

#endif

d_heap.h

/* d_heap.c */
/*
 * =====================================================================================
 *
 *       Filename:  d_heap.c
 *
 *    Description:  D堆实现
 *
 *        Version:  1.0
 *        Created:  2016-09-23 22:00
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  simon
 *
 * =====================================================================================
 */

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

#include "d_heap.h"

static void 
swap(Item *data, int a, int b)
{
    Item c;
    c = data[a];
    data[a] = data[b];
    data[b] = c;
}

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

/* 
 *        Name:  fixup
 * Description:  向上堆化
 * 
 */
static void 
fixup(d_heap_pt heap, int k)
{
    int degree = heap->degree;
    while (k>0 && item_cmp(heap->data[(k - 1) / degree],heap->data[k])) {
        swap(heap->data, k, (k - 1) / degree);
        k = (k - 1) / degree;
    }
}

/* 
 *        Name:  fixdown
 * Description:  向下堆化
 * 
 */
static void 
fixdown(d_heap_pt heap, int k, int end)
{
    int temp, tail, i;
    int degree = heap->degree;
    while (k*degree + 1 <= end) {
        tail = k*degree + degree;
        tail = tail < end ? tail : end;
        for (i = k*degree + 1, temp = i; i < tail; i++) {
            if (item_cmp(heap->data[temp], heap->data[i])) {
                temp = i;
            }
        }
        if (!item_cmp(heap->data[k], heap->data[temp])) {
            break;
        }
        swap(heap->data, k, temp);
        k = temp;
    }
}

d_heap_pt 
d_heap_init(int size, int degree)
{
    if (size<=0) {
        return NULL;
    }
    d_heap_pt heap_pt = malloc(sizeof(d_heap_t));
    heap_pt->data = malloc(sizeof(Item)*size);
    heap_pt->size = size;
    heap_pt->degree = degree;
    heap_pt->current = 0;
    return heap_pt;
}

void 
d_heap_free(d_heap_pt heap)
{
    if (heap == NULL) {
        return;
    }
    free(heap->data);
    free(heap);
}

void 
d_heap_print(d_heap_pt heap)
{
    int i;
    if (heap == NULL) {
        return;
    }
    for (i=0; i<heap->size; i++) {
        printf("%d ", heap->data[i]);
    }
}

int 
d_heap_push(d_heap_pt heap, Item element)
{
    if (heap == NULL) {
        return 0;
    }
    if (heap->current == heap->size) {
        return 0;
    }
    heap->data[heap->current] = element;
    fixup(heap, heap->current++);
    return 1;
}

Item 
d_heap_pop(d_heap_pt heap)
{
    swap(heap->data, 0, heap->current - 1);
    fixdown(heap, 0, heap->current - 2);
    return heap->data[--heap->current];
}

Item 
d_heap_top(d_heap_pt heap)
{
    return heap->data[0];
}

int
d_heap_empty(d_heap_pt heap)
{
    return heap->current == 0;
}

d_heap.c

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

#include "d_heap.h"

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

int
main(int argc, char *argv[])
{
    int a[] = {10, 20, 30, 50, 40, 70, 100, 130, 150, 60, 80, 170, 90, 110, 90};
    int i, len=LENGTH(a);
    d_heap_pt heap = NULL;
    heap = d_heap_init(LENGTH(a) + 10, 3);

    printf("\n== 空堆检查: %d", d_heap_empty(heap));
    printf("\n== 插入数据: ");
    for(i=0; i<len; i++)
    {
        printf("%d ", a[i]);
        d_heap_push(heap, a[i]);
    }
    printf("\n== 空堆检查: %d", d_heap_empty(heap));

    printf("\n== 最 小 堆: ");
    d_heap_print(heap);

    i=15;
    d_heap_push(heap, i);
    printf("\n== 添加元素: %d", i);
    printf("\n== 最 小 堆: ");
    d_heap_print(heap);

    printf("\n== 堆顶元素: %d\n", d_heap_top(heap));

    i = d_heap_pop(heap);
    printf("\n== 删除元素: %d", i);
    printf("\n== 最 小 堆: ");
    d_heap_print(heap);
    printf("\n");
    i = d_heap_pop(heap);
    printf("\n== 删除元素: %d", i);
    printf("\n== 最 小 堆: ");
    d_heap_print(heap);
    printf("\n");
    d_heap_free(heap);
    return 0;
}

d_heap_test.c

测试程序的运行结果

== 空堆检查: 1
== 插入数据: 10 20 30 50 40 70 100 130 150 60 80 170 90 110 90 
== 空堆检查: 0
== 最 小 堆: 10 20 30 50 40 70 100 130 150 60 80 170 90 110 90 0 0 0 0 0 0 0 0 0 0 
== 添加元素: 15
== 最 小 堆: 10 15 30 50 20 70 100 130 150 60 80 170 90 110 90 40 0 0 0 0 0 0 0 0 0 
== 堆顶元素: 10

== 删除元素: 10
== 最 小 堆: 15 20 30 50 40 70 100 130 150 60 80 170 90 110 90 10 0 0 0 0 0 0 0 0 0

== 删除元素: 15
== 最 小 堆: 20 40 30 50 90 70 100 130 150 60 80 170 90 110 15 10 0 0 0 0 0 0 0 0 0

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


Date 2016-09-23(星期五) 19:13 By Ming In 算法 Tags 算法,