C 算法精介----堆->分析与实现

堆的描述
  
    堆是一种二叉树结构,通常是子节点的数值比父节点的值小,所以根节点是树种最大的节点。同样也可以说子节点的数值比父节点的数字大,所以根节点是树中最小的结点。子节点值比父节点值小的堆叫最大堆,反之,最小堆。下面来看下堆的性质:
父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
    堆的存储
   一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。下面是一个最大堆的图,及堆的遍历顺序


//
//  Heap.h
//  Algorithms&Data_structures
//
//  Created by TTc on 15-2-13.
//  Copyright (c) 2015年 TTc. All rights reserved.
//

#ifndef __Algorithms_Data_structures__Heap__
#define __Algorithms_Data_structures__Heap__

#include <stdio.h>

/*定义一个堆得结构体*/

typedef struct Heap_{
    
    /*堆中元素的个数*/
    int size;
    /* 比较节点和插入数值大小的函数指针*/
    int (*compare)(const void *key1,const void *key2);
    /*当*tree是动态申请空间时,用来释放空间的函数指针*/
    void (*destroy)(void*data);
    /*tree 是堆中存储数据节点指针的数组*/
    void **tree;
}Heap;


/*函数接口*/
void heap_init(Heap *heap,int  (*compare)(const void *key1,const void *key2),void (*destroy)(void *data));

void heap_destroy(Heap *heap);

int heap_insert(Heap *heap,const void *data);

int heap_extract(Heap *heap,void **data);

#define heap_size(heap)   ((heap)->size)


#endif /* defined(__Algorithms_Data_structures__Heap__) */

//
//  Heap.c
//  Algorithms&Data_structures
//
//  Created by TTc on 15-2-13.
//  Copyright (c) 2015年 TTc. All rights reserved.
//

#include "Heap.h"
#include <string.h>
#include <stdlib.h>
/*
堆的性质:     父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
              每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
 

堆的存储 :     一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。
              它的左右子结点下标分别为2 * i + 1和2 * i + 2。
              如第0个结点左右子结点下标分别为1和2。
 */


/*当前节点父节点的下标*/
#define heap_parent(npos)  ((int) (((npos)-1)/2))
/*当前节点左子树下标*/
#define heap_left(npos) (((npos)*2) + 1)
/*当前节点右子树的下标*/
#define heap_right(npos) (((npos))*2+2)

/*
  如果堆为最大值堆,当key1>key2 时返回1
                 当key1<key2 时返回-1
                 当key1=key2 时返回0
 */
int
compare(const void *key1,const void *key2){
    if(key1 - key2 > 0){
        return 1;
    }else if(key1 - key2 < 0){
        return -1;
    }else{
        return 0;
    }
};



/*  初始化 */
//O(1)
void
heap_init(Heap *heap,int  (*compare)(const void *key1,const void *key2),
          void (*destroy)(void *data)){
    heap->size = 0;
    heap->destroy = destroy;
    heap->compare = compare;
    heap->tree = NULL;
}

void
heap_destroy(Heap *heap){
    //remove all node
    if(heap->destroy != NULL){
        for (int i = 0; i< heap_size(heap); i++) {
            heap->destroy(heap->tree[i]);
        }
    }
    free(heap->tree);
    memset(heap, 0, sizeof(Heap));
}


/* 将数据插入到最大值堆中
 * 返回值:成功0;失败-1
 *  1:要为新的节点分配内存空间,以保证能容纳此节点
    2:新的节点先存储再数组的末尾,此时将破坏掉堆的固有特性,所以我们必须调整树的结构,对节点进行重新排序
 */
//O(lg n) n = heap中的节点个数
int
heap_insert(Heap *heap,const void *data){
    void *temp;
    int ipos,ppos;
    /*插入的数据申请空间*/
    if((temp = (void *)realloc(heap->tree, (heap_size(heap)+1)*sizeof(void*))) == NULL){
        return -1;
    }
    heap->tree = temp;
    
    //insert data
    heap->tree[heap_size(heap)] = (void*)data;
    /* ipos : 保存插入数据节点的下标
     * ppos: 保存插入数据父节点的下标
     */
    ipos = heap_size(heap);
    ppos = heap_parent(ipos);
    
    //将当前要插入的 新的节点  与 父节点进行比较 如果返回-1则停止 否则重复该操作
    while (ipos >0 && heap->compare(heap->tree[ppos],heap->tree[ipos]) < 0 ) {
        //交换数据
        temp = heap->tree[ppos];
        heap->tree[ppos] = heap->tree[ipos];
        heap->tree[ipos] = temp;
        
        ipos = ppos;
        ppos = heap_parent(ipos);
    }
    heap->size++;
    return 0;
}

/*释放堆顶部的节点*/


/*
 1:首先将data指向想要删除的 root节点 
 2:接下来保存最后一个节点的内容,并且将树的大小减1
 3:重新为 二叉树 分配较小的内存空间
 4:再确定前3步 曹祖都成功之后,将最后一个节点的内容copy到root节点中
 5:显然这个过程破坏了 堆的特性,所以我们必须调整树的结构,对节点重新排列
 
 
 6:为了重新排列一棵树,从根节点开始 沿树干层层向下移动,与节点的两个子节点进行比较。
 7:在每一个层上,如果父节点与子节点的位置不正确,就交换节点内容,此时需要将父节点与
    位置最混乱的哪个子节点进行交换swap。
 8:最后,通过递减堆数据结构中的size成员更新堆的容量
 
 */
//O(lg n)
int
heap_extract(Heap *heap,void **data){
    void *save,*temp;
    int ipos,lpos,rpos,mpos;
    /*当堆为空时直接返回-1*/
    if(heap_size(heap) == 0){
        return -1;
    }
    /*获得根节点的数值*/
    *data = heap->tree[0];
    /*调整堆的存储空间大小save 指向tree的最后一个数据*/
    save = heap->tree[heap_size(heap) - 1];
    
    if(heap_size(heap)- 1 > 0){
        //重新为 二叉树 分配较小的内存空间
        if((temp = (void**)realloc(heap->tree, (heap_size(heap) - 1)*sizeof(void *)))== NULL){
            return -1;
        }
        heap->tree = temp; // 重新分配内存
        heap->size --;    //-1
    }else{
        free(heap->tree);
        heap->tree = NULL;
        heap->size = 0;
        return 0;
    }
    
    heap->tree[0] = save;
    ipos = 0;
    while (1){
        //获取当前节点的 左右子节点
        lpos = heap_left(ipos);
        rpos = heap_right(ipos);
        //左子节点 下标  < 堆的size  && key1> key2
        if (lpos < heap_size(heap) && heap ->compare(heap->tree[lpos],heap->tree[ipos]) > 0){
            mpos = lpos;
        } else {
            mpos = ipos;
        }
        
        if (lpos < heap_size(heap) && heap ->compare(heap->tree[rpos],heap->tree[ipos]) > 0){
            mpos = rpos;
        }
        
        if (mpos == ipos){
            break;
        } else {
            temp = heap->tree[mpos];
            heap->tree[mpos] = heap->tree[ipos];
            heap -> tree[ipos] = temp;
            
            ipos = mpos;
        }  
    }
    return 0;
}


















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值