1.堆的概念和作用

   堆是一种树形结构,能够让人快速的确定最大值或最小值的节点,也就是数据。并且一棵树的代价是小于一个有序数组的代价的。堆是一颗二叉树,通常其子节点存储的值比父节点的值小(最大值堆,根节点数据最大),最小值堆(根节点数据最小)时,则情况相反。这样的二叉树是局部有序的,任何一哥节点与其兄弟节点之间都没有必然的顺序关系,但它与其父子节点有大小顺序关系。

  堆是左平衡(一颗平衡树最后一行叶子结点都是偏左的的树)的树,随着节点的增加,树会逐级从左到右增长。因此通常用一个数组存储一个左平衡树。则处在i处的节点,其父节点位于(i-1)/2的整数位置,左节点为2i+1,右节点为2i+2。

算法精解(八):C语言描述(堆)

2.代码实现

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

typedef struct Heap_
{
	int size;
	int(*compare)(const void *key1, const void *key2);
	void(*destroy)(void *data);
	void **tree;
}Heap;

#define heap_parent(npos) ((int)(((npos)-1)/2))   //父节点
#define heap_left(nops) (((nops)*2)+1)           //左节点
#define heap_right (((nops)*2)+2)               //右节点
#define heap_size(heap) (heap->size)

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

//销毁堆
void heap_destroy(Heap *heap)
{
	int i;
	if (heap->destroy != NULL)
	{
		for (i = 0; i < heap_size(heap); i++)
		{
			heap->destroy(heap->tree[i]);
		}
	}

	free(heap->tree);
	memset(heap, 0, sizeof(Heap));
	return;
}

//插入元素成功,返回0 ,否则返回-1       复杂度 lg(n)
int heap_insert(Heap *heap, const void *data)
{
	void **temp;
	int ipos, ppos;

	// 添加节点时,先使用realloct在原本的空间上添加空间
	if((temp = (void **)realloc(heap->tree,(heap_size(heap)+1)*sizeof(void *))) == NULL)
	{
		//添加空间失败
		return  -1;
	}
	else
	{
		//成功时,将新的空间temp赋给data
		heap->tree = temp;
	}
	//将要插入的数据添加在数组的最后一位
	heap->tree[heap_size(heap)] = (void *)data;

	ipos = heap_size(heap);   //新插入节点的位置
	ppos = heap_parent(ipos); //新插入节点的父节点

	//如果新节点存在父节点,且父节点小于新节点,则交换两个节点的位置
	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;
}

//释放节点,成功0,失败-1  lg(n)
int heap_extract(Heap *heap, void **data)
{
	void *save, **temp;
	int ipos, lpos, rpos, mpos;

	if (heap_size(heap) == 0)
		return -1;
	*data = heap->tree[0];  //取根节点,即最大值
	save = heap->tree[heap_size(heap) - 1]; //取数组最后一位,即堆最后一位
	//如果存在节点大于0
	if (heap_size(heap) - 1 > 0)
	{
		if ((temp = (void **)realloc(heap->tree, (heap_size(heap) - 1) * sizeof(void **))) == NULL)
		{
			return -1;
		}
		else
		{
			heap->tree = temp;
		}
		heap->size--;
	}
	else
	{
		//存在数据为0
		free(heap->tree);
		heap->tree = NULL;
		heap->size = 0;
		return 0;
	}

	//取最大值后,调节堆的数据存放,调节堆的最大值
	heap->tree[0] = save;   //最后一个节点变为根节点
	ipos = 0;
	lpos = heap_left(ipos);
	rpos = heap_right(ipos);

	while (1)
	{
		lpos = heap_left(ipos);
		rpos = heap_right(ipos);
		//如果根节点的左节点存在,且左节点数据大于根节点数据
		if(lpos < heap_size(heap) && heap->compare(heap->tree[lpos],heap->tree[ipos]) > 0)
		{
			mpos = lpos;  
		}
		else
		{
			mpos = ipos;
		}

		//经过上面的判断,大的那一个节点再和右节点比较
		if (rpos < heap_size(heap) && heap->compare(heap->tree[rpos], heap->tree[mpos]) > 0)
		{
			mpos = rpos;
		}
		//两次比较之后,最大节点的位置为mpos
		if (mpos == ipos)
		{
			//如果根节点大于左右节点,则成立,退出循环
			break;
		}
		else
		{
			//否则,继续交换根节点和最大节点的位置,循环查找
			*temp = heap->tree[mpos];
			heap->tree[mpos] = heap->tree[ipos];
			heap->tree[ipos] = *temp;

			ipos = mpos;
		}
	}
	return 0;
}

 

相关文章: