优先队列之左式堆

1.左式堆的性质
我们把任一节点X的零路径长(null path length,NPL)Npl(X)定义为从X到一个没有两个儿子的节点的最短路径的长。因此,具有0个或1个儿子的节点的Npl为0,而Npl(NULL) = -1。
左式堆性质是:对于堆中的每一个节点X,左儿子的零路径长至少与右儿子的零路径长一样大。下图左边是左式堆,右边不是。

2.左式堆的操作
(1)合并
合并是左式堆最重要的操作。插入只是合并的特殊情况。下面采用递归来演示如何进行左式堆的合并。下图是左式堆H1和左式堆H2。

如果这两个堆中有一个堆是空的,那么我们可以返回另一个堆。否则为了合并这两个堆,我们需要比较它们的根。首先,我们将具有大的根值的堆与具有小的根值的堆的右子堆合并。在本例中,我们递归地将H2与H1中根在8处的右子堆合并。得到下图。

由于这棵树是递归形成的,而我们尚未对算法描述完毕,因此我们还不能说明该堆是如何得到的。不过,有理由假设,最后的结果是一棵左式堆,因为它是通过递归的步骤得到的。现在我们让这个新的堆成为H1的右儿子,得到下图。

虽然最后得到的堆满足堆序性质,但是,它不是左式堆,因为根的左子树的零路径长为1而根的右子树的零路径长为2。由于此时左右子树都是左式堆,所以只要交换根的左右儿子即可。下图得到最后的结果。
将算法的描述直接翻译成代码。除了增加Npl域之外,算法中的类型定义与二叉树是相同的。另外当交换左右子树的时候,注意更新Npl的值。
(2)插入
插入只是特殊的合并的特殊情况,因为我们可以把插入看成单节点堆与一个大的堆的Merge。
3.左式堆的实现
3.1 头文件
//
//  LeftHeap.h
//  LeftHeap
//
//  Created by Wuyixin on 2017/5/31.
//  Copyright © 2017年 Coding365. All rights reserved.
//

#ifndef LeftHeap_h
#define LeftHeap_h

#include <stdio.h>


/* 用来标记不合法的元素的值 */
extern const int ELEM_MIN;

struct TreeNode;
typedef struct TreeNode *PriorityQueue;

typedef int ElemType;
/* 初始化 */
PriorityQueue initialize();
/* 查找最小值 */
ElemType find_min(PriorityQueue h);
/* 是否空 */
int is_empty(PriorityQueue h);
/* 合并堆 */
PriorityQueue merge(PriorityQueue h1,PriorityQueue h2);
/* 插入元素 */
PriorityQueue insert(ElemType x,PriorityQueue h);
/* 删除最小元 */
PriorityQueue delete_min(PriorityQueue h);
/* 遍历 */
void print_heap(PriorityQueue h);

struct TreeNode
{
    ElemType elem;
    PriorityQueue left;
    PriorityQueue right;
    int npl;/*零路径长(null path length)*/
};

#endif /* LeftHeap_h */



3.2 实现文件
//
//  LeftHeap.c
//  LeftHeap
//
//  Created by Wuyixin on 2017/5/31.
//  Copyright © 2017年 Coding365. All rights reserved.
//

#include "LeftHeap.h"
#include <stdlib.h>
#include <limits.h>

const int ELEM_MIN = INT_MIN;

void error(char* message){
    printf("%s/n",message);
}

void fatal_error(char* message){
    printf("%s/n",message);
    exit(EXIT_FAILURE);
}
/* 初始化 */
PriorityQueue initialize(){
    PriorityQueue h = (PriorityQueue)malloc(sizeof(struct TreeNode));
    if (h == NULL)
        fatal_error("out of space!!!");
    h->left = NULL;
    h->right = NULL;
    h->npl = -1;//空节点
    return h;
}
/* 查找最小值 */
ElemType find_min(PriorityQueue h){
    if (is_empty(h)) return ELEM_MIN;
    return h->elem;
}
/* 是否空 */
int is_empty(PriorityQueue h){
    return h == NULL || h->npl == -1;
}

/* 是否头节点*/
int is_head(PriorityQueue h){
    return h != NULL && h->npl == -1 && h->left == NULL && h->right == NULL;
}

static void swap_children(PriorityQueue h){
    PriorityQueue temp = h->left;
    h->left = h->right;
    h->right = temp;
}

static PriorityQueue _merge(PriorityQueue h1,PriorityQueue h2){
    /* 单节点,将h2赋给h1的左子树,左子树的节点数一定要大于等于右子树*/
    if (h1->left == NULL)
        h1->left = h2;
    else{
        h1->right = merge(h1->right, h2);/* 注意调用merge而不是_merge */
        if (h1->left->npl < h1->right->npl)
            swap_children(h1);
        h1->npl = h1->right->npl + 1;
    }
    return h1;
}

/* 合并堆 */
PriorityQueue merge(PriorityQueue h1,PriorityQueue h2){
    if (h1 == NULL)
        return h2;
    if (h2 == NULL)
        return h1;
    if (h1->elem < h2->elem)
        return _merge(h1, h2);
    else
        return _merge(h2, h1);
}
/* 插入元素 */
PriorityQueue insert(ElemType x,PriorityQueue h){
    
    if (is_head(h)){
        h->elem = x;
        h->npl = 0;
        return h;
    }
    
    PriorityQueue singlenode = (PriorityQueue)malloc(sizeof(struct TreeNode));
    if (singlenode == NULL)
        fatal_error("out of space!!!");
    else{
        singlenode->elem = x;
        singlenode->npl = 0;
        singlenode->left = NULL;
        singlenode->right = NULL;
        h = merge(singlenode, h);
    }
    
    return h;
}
/* 删除最小元 */
PriorityQueue delete_min(PriorityQueue h){
    if (is_empty(h)){
        error("Priority queue is empty");
        return h;
    }
    
    PriorityQueue left,right;
    left = h->left;
    right = h->right;
    free(h);
    return merge(left, right);
}


static void _print_heap(PriorityQueue h,int type){
    if (!is_empty(h)){
        char* s_type = "";
        if (type == 1) s_type = "root";
        else if(type == 2) s_type = "left";
        else if (type == 3) s_type = "right";
        printf("(elem:%d,npl:%d,type:%s) ",h->elem,h->npl,s_type);
        _print_heap(h->left,2);
        _print_heap(h->right,3);
    }
}

/* 遍历 */
void print_heap(PriorityQueue h){
    _print_heap(h, 1);
}







3.3 调用
//
//  main.c
//  LeftHeap
//
//  Created by Wuyixin on 2017/5/31.
//  Copyright © 2017年 Coding365. All rights reserved.
//

#include <stdio.h>
#include "LeftHeap.h"
int main(int argc, const char * argv[]) {
    
    PriorityQueue h1 = initialize();
    h1 = insert(1, h1);
    h1 = insert(2, h1);
    h1 = insert(3, h1);
    h1 = insert(4, h1);
    h1 = insert(5, h1);
    print_heap(h1);
    
    PriorityQueue h2 = initialize();
    h2 = insert(6, h2);
    h2 = insert(7, h2);
    h2 = insert(8, h2);
    h2 = insert(9, h2);
    h2 = insert(10, h2);
    printf("\n");
    print_heap(h2);
    
    printf("\n");
    print_heap(merge(h1, h2));
    
    return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值