首页 > 代码库 > 二叉树学习之堆排序

二叉树学习之堆排序

认识堆是从堆排序开始的


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

根结点下标为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/

二叉树学习之堆排序