二叉树学习之堆排序

认识堆是从堆排序开始的

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

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

时间: 2024-10-13 02:22:32

二叉树学习之堆排序的相关文章

排序算法学习之堆排序

一.堆与堆排序的产生及定义 在简单选择排序中,每次从n个元素中比较n-1次选取最小的元素,这很好理解,但是前面比较过的数据在之后还要重新比较,这将花费大量的运算时间.堆排序算法就很好的解决了这个问题,堆排序在每次选择到最小记录的同时会根据比较结果对其他数据进行调整,堆排序的时间复杂度为O(NlogN). 堆通常是指二叉堆,即堆是一颗完全二叉树,同时其满足一定性质:每个节点的值大于等于其左右孩子的值(大顶堆),或者每个节点的值小于等于其左右孩子的值(小顶堆).堆在本质上是一个数组,根节点即为a[0

二叉树学习笔记。

1. 层序构建和先序遍历: 1 public class Tree { 2 public Tree left; 3 public Tree right; 4 public int val; 5 6 public Tree() { 7 8 } 9 10 public Tree(String n) { 11 this.val = Integer.parseInt(n); 12 } 13 14 public Tree createTreeByLevel(String[] var) { 15 if (v

二叉树学习笔记之二叉查找树(BSTree)

二叉查找树即搜索二叉树,或者二叉排序树(BSTree),学习回顾一下有关的知识. >>关于二叉查找树 二叉查找树(Binary Search Tree)是指一棵空树或者具有下列性质的二叉树:1. 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值:2. 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值:3. 任意节点的左.右子树也分别为二叉查找树.4. 没有键值相等的节点,这个特征很重要,可以帮助理解二叉排序树的很多操作.二叉查找树具有很高的灵活性,对其优化可

二叉树学习之非递归遍历

二叉树递归遍历可谓是学过数据结构的同仁都能想一下就能写出来,但在应聘过程我们常常遇到的是写出一个二叉树非递归遍历函数,接着上篇文章写二叉树的非递归遍历,先难后易,一步一步的来. 先上代码: #include "binarytree.h" #include <stack> #include <queue> #ifndef RECU #warning("RECU is not defined") /** *前序遍历(根左右) * *1.当前节点为

二叉树学习笔记1

数组.向量.链表都是一种顺序容器,它们提供了按位置访问数据的手段.而很多情况下,我们需要按数据的值来访问元素,而不是它们的位置来访问元素.比如有这样一个数组int num[3]={1,2,3},我们可以非常快速的访问数组中下标为2的数据,也就是说我们知道这个数据的位置,就可以快速访问.有时候我们是不知道元素的位置,但是却知道它的值是多少.假设我们有一个变量,存放在num这个数组中,我们知道它的值为2,却不知道它下标是多少,也就是说不知道它的位置.这个时候再去数组中访问这个元素就比较费劲,就得遍历

第5章 树与二叉树学习小结

前几章学习的基本都是线性的数据结构,就有顺序存储结构和链式存储结构,而这一章“树”结构是一类非线性数据结构,跟之前就有不同的点,但是,树的存储结构还是可以通过找到元素之间逻辑关系,采用类似线性表的方式,按照结点之间的逻辑关系放到线性存储中. 这部分主要学习到二叉树的内容,二叉树有好几个性质,我想这些性质很重要,有时候在解决问题,它能够帮助理解这棵树比较抽象的结构层次,这是我在理解代码时候体会到的.二叉树存储结构跟遍历有很大的关系,遍历的结果是将非线性结构的树中结点排成一个线性序列. 这是二叉链表

二叉树学习二:二叉搜索树

二叉搜索树(Binary Search Tree),或者是一棵空树,或者: 1)若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 2)若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值: 3)二叉搜索树的左.右子树也分别为二叉搜索树. 搜索二叉树相关的算法实现: 1)搜索二叉树的创建与转化为双链表实现: 1 #include "stdafx.h" 2 #include<iostream> 3 using namespace std; 4 5 /*二无

二叉树学习笔记-实现

上一篇文章中,算是初步了解了二叉树是一种怎样的数据结构,也算是有了一个初步的印象.接下来用自己的代码去实现一个二叉搜索树(以下全叫二叉树)类,对外提供常用的接口,比如insert.erase.size.find等等.就像盖房一样,如果说二叉树是一座建筑,那么其中的节点就是一块块砖.要实现二叉树这个类,就必须先实现节点类,假设我们起名为treeNode.在STL标准库中,像一般的数据结构都是模板类,在这里为了方便起见,假设二叉树这个类中保存的数据全是int型. 这个节点类中,需要包含如下的一些成员

二叉树学习笔记之树的旋转

树旋转(Tree rotation)是二叉树中的一种子树调整操作,每一次旋转并不影响对该二叉树进行中序遍历的结果.树旋转通常应用于需要调整树的局部平衡性的场合. >>左旋和右旋 树的旋转有两种基本的操作,即左旋(逆时针方向旋转)和右旋(顺时针方向旋转). 树旋转包括两个不同的方式,分别是左旋转(以P为转轴)和右旋转(以Q为转轴).两种旋转呈镜像,而且互为逆操作. 下图示意了两种树旋转过程中, 子树的初态和终态 +---+ +---+ | Q | | P | +---+ +---+ / \ ri