认识堆是从堆排序开始的
根结点下标为0时,下标为n的元素的子结点下标分别为2*n+1,2*n+2,其父结点下标为(n-1)/2
1、父结点的键值总是>=(<=)任何一个子结点的键值
2、每个结点的左右子树都是二叉堆
当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆
堆的操作方法如下定义:
#ifndef _HEAP_CTL_H
#define _HEAP_CTL_H
#ifdef __cplusplus
extern "C" {
#endif
void heapPprint(const char *info, int a[], int nLen);
void swap(int *a, int *b);
void buildMaxHeap(int *a, int hLen);
void buildMinHeap(int *a, int hLen);
void minHeapAddNumber(int *a, int *nLen, int newNum);
void maxHeapAddNumber(int *a, int *nLen, int newNum);
int minHeapDelNumber(int *a, int *nLen);
int maxHeapDelNumber(int *a, int *nLen);
void ascHeapSort(int *iArray,int nLen);
void descHeapSort(int *iArray,int nLen);
#ifdef __cplusplus
}
#endif
#endif
函数实现如下:
#include <stdio.h>
#include "heapctl.h"
static void __pprint(int a[], int i, int nLen)
{
printf("(");
if (i < nLen) {
printf("%d", a[i]);
__pprint(a, i*2+1, nLen);
__pprint(a, i*2+2,nLen);
}
printf(")");
}
/**
*@brief 输出堆用于tree工具来图形化显示
*
*@params info 写在前面的话
*@params a 所要打印的数组
*@params nLen 数组的长度
*/
void heapPprint(const char *info, int a[], int nLen)
{
if (info)
printf("%s", info);
printf("\n\\tree");
__pprint(a, 0, nLen);
printf("\n");
}
/**
*@brief 交换两个数据
*
*@params a 用以交换的第一个数据
*@params b 用以交换的第二个数据
*/
void swap(int *a, int *b)
{
#if 0//利用了辅助空间tmp
int tmp = *a;
*a = *b;
*b = tmp;
#endif
#if 0//相加可能溢出
*a += *b;
*b = *a - *b;
*a -= *b;
#endif
#if 1//异或运算A^B^B=A
*a ^= *b;
*b ^= *a;
*a ^= *b;
#endif
}
/**
*@brief 大顶堆调整
*
*@params A 数组A
*@params hLen
*@params 需要调整的节点i
*/
void maxHeapAdjust(int *a,int i,int size) //调整堆
{
int lchild=2*i+1; //i的左孩子节点序号
int rchild=2*i+2; //i的右孩子节点序号
int max=i; //临时变量
if(i <= size/2) //如果i是叶节点就不用进行调整
{
if(lchild<size && a[lchild]>a[max]) {
max=lchild;
}
if(rchild<size && a[rchild]>a[max]) {
max=rchild;
}
if(max != i) {
swap(&a[i], &a[max]);
maxHeapAdjust(a,max,size); //避免调整之后以max为父节点的子树不是堆
}
}
}
/**
*@brief 构建大顶堆
*
*@params a 数组a
*@params hLen 数组元素的个数
*/
void buildMaxHeap(int *a, int hLen)
{
int i;
//堆类似完全二叉树,nLen 为偶数:
//深度为2的节点数n2 = nLen>>1 -1, n1 = 1, n0 = nLen>>1;
//nLen 为奇数, n2 = nLen/2, n1 = 0, n0 = nLen/2+1;
//n0 = n2 + 1
//从非叶节点最大序号位置调整, 值为size/2
for(i=hLen/2-1; i>=0; i--)
{
maxHeapAdjust(a,i,hLen);
}
}
/**
*@brief 小顶堆调整
*
*@params A 数组A
*@params hLen
*@params 需要调整的节点i
*/
void minHeapAdjust(int *a,int i,int size) //调整堆
{
int lchild=2*i+1; //i的左孩子节点序号
int rchild=2*i+2; //i的右孩子节点序号
int max=i; //临时变量
if(lchild<size && a[lchild]<a[max]) {
max=lchild;
}
if(rchild<size && a[rchild]<a[max]) {
max=rchild;
}
if(max != i) {
swap(&a[i], &a[max]);
minHeapAdjust(a, max, size); //避免调整之后以max为父节点的子树不是堆
}
}
/**
*@brief 构建大顶堆
*
*@params a 数组a
*@params hLen 数组元素的个数
*/
void buildMinHeap(int *a, int hLen)
{
int i;
//堆类似完全二叉树,nLen 为偶数:
//深度为2的节点数n2 = nLen>>1 -1, n1 = 1, n0 = nLen>>1;
//nLen 为奇数, n2 = nLen/2, n1 = 0, n0 = nLen/2+1;
//n0 = n2 + 1
//从非叶节点最大序号位置调整, 值为size/2
for(i=hLen/2-1; i>=0; i--)
{
minHeapAdjust(a,i,hLen);
}
}
/**
*@brief 向小顶堆中插入数据
*
*@params a 要插入数据的数组
*@params nLen 数组元素长度指针, 插入后会自增
*@params newNum 插入的元素值
*
*1、将插入的数据放入数组的末尾
*2、根据与其父节点的大小来调整小顶堆
*/
void minHeapAddNumber(int *a, int *nLen, int newNum)
{
a[*nLen] = newNum;
int j, i = *nLen;
for (j = (i-1)/2;
(j >= 0 && i != 0) && a[i] < a[j];
i = j, j = (i-1)/2 )
swap(&a[i], &a[j]);
++*nLen;
}
/**
*@brief 向大顶堆中插入数据
*
*@params a 要插入数据的数组
*@params nLen 数组元素长度指针, 插入后会自增
*@params newNum 插入的元素值
*
*1、将插入的数据放入数组的末尾
*2、根据与其父节点的大小关系调整大顶堆
*/
void maxHeapAddNumber(int *a, int *nLen, int newNum)
{
a[*nLen] = newNum;
int j, i = *nLen;
for (j = (i-1)/2;
(j >= 0 && i != 0) && a[i] > a[j];
i = j, j = (i-1)/2 )
swap(&a[i], &a[j]);
++*nLen;
}
/**
*@brief 小顶堆的删除操作,堆中每次都只能删除第0个数据,
*
*@params a 要删除数据的数组
*@params nLen 数组元素长度指针, 插入后会自减
*
*1、将插入的数据放入数组的末尾
*2、根据与其父节点的大小关系调整大顶堆
*/
int minHeapDelNumber(int *a, int *nLen)
{
int newLen = *nLen - 1;
swap(&a[0], &a[newLen]);
minHeapAdjust(a, 0, newLen);
*nLen = newLen;
return a[newLen];
}
int maxHeapDelNumber(int *a, int *nLen)
{
int newLen = *nLen - 1;
swap(&a[0], &a[newLen]);
maxHeapAdjust(a, 0, newLen);
*nLen = newLen;
return a[newLen];
}
/**
*@brief 利用大顶堆进行升序排列
*
*@params a 所要排序的数组名称
*@params nLen 数组中元素的个数
*/
void ascHeapSort(int *a,int nLen)
{
int i;
buildMaxHeap(a,nLen);
for(i=nLen-1; i>=1; i--)
{
swap(&a[0], &a[i]); //交换堆顶和最后一个元素,即每次将剩余元素中的最大者放到最后面
maxHeapAdjust(a, 0, i); //重新调整堆顶节点成为大顶堆
}
}
/**
*@brief 利用小顶堆进行降序排列
*
*@params a 所要排序的数组名称
*@params nLen 数组中元素的个数
*/
void descHeapSort(int *iArray,int nLen)
{
int i;
buildMinHeap(iArray, nLen);
for(i=nLen-1; i>=1; i--) {
swap(&iArray[0], &iArray[i]);
minHeapAdjust(iArray, 0, i);
}
}
函数的实现方法与应用请自行参照注释
或以下文档
http://www.cnblogs.com/dolphin0520/archive/2011/10/06/2199741.html
http://blog.youkuaiyun.com/morewindows/article/details/6709644/