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