您的位置:首页 > 其它

二叉树学习之堆排序

2014-11-04 15:32 344 查看
认识堆是从堆排序开始的

二叉堆是完全二叉树或者是近似完全二叉树,堆存储在数组中:

根结点下标为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.csdn.net/morewindows/article/details/6709644/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: