简单堆的创建和操作

回顾前面的知识,我们学了二叉树,而二叉树有很多种存储方式,比如一维数组存储,

链表存储,在刚刚学习建立二叉树的时候,我们用的是链表存储的方式,也就是利用结构体定义一个二

叉树节点,然后将这些节点连接起来。现在为了更好地存储二叉树,我们学习了堆,即将二叉树存储在

一个一维数组里面,由于按照不同的存储顺序,可以将一个堆分为最大堆和最小堆。

最大堆:每个父节点必须大于左右孩子,而每个孩子所代表的子树也是最大堆

最小堆:每个父节点必须小于左右孩子,而每个孩子所代表的子树也是最小堆

那么如何将一个堆变成一个最大堆或者最小堆呢,就是通过向下调整法或者向上调整法,下面会做详细的说明。

首先我们来举一个栗子,给出如下一棵二叉树:

首先我们需要一个数组将这个二叉树存储起来,因为vector的操作与顺序表相似,为了简便,我们调用

库里的vector来存储二叉树,只不过存储类型为模板类T,此时我们默认建最大堆,所以要提供过向下调

整法来调整,为了使每棵子树都是父节点最大,我们先从最后一个节点找起,然后找到该节点的父节

点,比较父节点和两个子节点的大小,若左右节点有一个比父节点大,则和父节点交换值,然后依次

往前比较,直到整个堆调整为最大堆。

代码如下:

#pragma once
#include<assert.h>
#include<vector>

using namespace std;

template<class T>
class Heap
{
public:
	Heap()
	{}
	//建堆
	Heap(const T* a,size_t size)
	{
		for (size_t i = 0; i < size; i++)//将数组中的数据放到堆里去
		{
			_a.push_back(a[i]);
		}

		for (int j = (_a.size() - 2) / 2; j >= 0; j--)  //第一个非叶子结点的父亲开始
		{
			AdjustDown(j);
		}
	}

protected:
	void AdjustDown(size_t parent)
	{
		int child = parent * 2 + 1;; //找到左孩子

 		while (child< _a.size())
		{
			if ((child + 1 < _a.size())&&_a[child] < _a[child + 1] )  //找到左右孩子较大的一个
			{
				++child;
			}

			if (_a[child] > _a[parent])   //如果孩子比父亲大,交换孩子和父亲的值
			{
				swap(_a[child], _a[parent]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}
protected:
	vector<T> _a;
};

通过调整整个堆变为最大堆,调整后的二叉树如下所示

那么建立好堆之后,在对数据进行操作的时候对堆也有一定的影响,所以下面我们来简单写一下堆的pop和push。

push:可以直接调用vector的push_back(),然后再通过向上调整法调整变成最大堆

pop:由于vector没有从堆前面直接pop的,所以要将堆的第一个元素与最后一个元素调换位置,再通过pop_back()pop出去,再通过调整变成最大堆。

具体代码如下:

void push(const T& x)
	{
		_a.push_back(x);

		AdjustUp(_a.size() - 1);
	}
	void pop()
	{
		assert(!_a.empty());

		swap(_a[0], _a[_a.size() - 1]);  //由于没有头删函数,将第一个数据和最后一个交换,再尾删
		_a.pop_back();

		for (int j = (_a.size() - 2) / 2; j >= 0; j--)  //调整为最大堆
		{
			AdjustDown(j);
		}
	}

protected:
	void AdjustDown(size_t parent)
	{
		int child = parent * 2 + 1;; //找到左孩子

 		while (child< _a.size())
		{
			if ((child + 1 < _a.size())&&_a[child] < _a[child + 1] )  //找到左右孩子较大的一个
			{
				++child;
			}

			if (_a[child] > _a[parent])   //如果孩子比父亲大,交换孩子和父亲的值
			{
				swap(_a[child], _a[parent]);
				parent = child;
				child = parent * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}

	void AdjustUp(size_t child)
	{
		int parent = (child - 1) / 2;

		while (child>0)
		{
			if (_a[child]>_a[parent])
			{
				swap(_a[child], _a[parent]);
				child = parent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}

以上便是堆的建立以及简单的操作,小伙伴们看明白了么?

下面给出测试代码:

#include"Heap.h"

void test()
{
	int array[10] = { 7, 14, 12, 15, 10, 11, 13, 16, 9, 8 };
	Heap<int> hp1(array, 10);
	hp1.push(17);
	hp1.pop();   
}

int main()
{
	test();
	return 0;
}

由于这里只给出了具体方法,类的成员没有给完全,小伙伴们可以下去自行补全哦,重要的是方法,可能我给出的方法也有一定的不足之处,还希望大家指出共同进步!

时间: 2024-10-15 07:02:14

简单堆的创建和操作的相关文章

源码分析:Java堆的创建

虚拟机在内存中申请一片区域,由虚拟机自动管理,用来满足应用程序对象分配的空间需求,即堆空间. 由于程序运行的局部特性,程序创建的大多数对象都具有非常短的生命周期,而程序也会创建一些生命周期特别长的对象.简单的复制收集器无论对象的 生命周期是长是短,都会进行复制操作.而生命周期较长的对象在多次垃圾回收期间内并不会被回收,这就使得这些对象被来回复制而使得算法性能大大下降. 分代收集把堆分为多个子堆,分别用来存放不同寿命的对象.新生对象空间的将经历最频繁的垃圾回收,而对于经历了若干次垃圾收集后仍然存活

用C++进行简单的文件I/O操作-转自VC知识库

原文请见 http://www.vckbase.com/index.php/wv/1158 序论 我曾发表过文件输入输出的文章,现在觉得有必要再写一点.文件 I/O 在C++中比烤蛋糕简单多了. 在这篇文章里,我会详细解释ASCII和二进制文件的输入输出的每个细节,值得注意的是,所有这些都是用C++完成的. 一.ASCII 输出 为了使用下面的方法, 你必须包含头文件(译者注:在标准C++中,已经使用取代,所有的C++标准头文件都是无后缀的.).这是 的一个扩展集, 提供有缓冲的文件输入输出操作

MATLAB编程与应用系列-第2章 数组及矩阵的创建及操作(1)

本系列教程来源于出版设计<基于MATLAB编程基础与典型应用书籍>,如涉及版权问题,请联系:[email protected]. 出版社:人民邮电出版社, 页数:525. 本系列教程目前基于MATLABR2006a,可能对于更高级版本的功能和函数有差异,教程中如有问题,请联系:[email protected] MATLAB中的数学计算部分包括数值计算和符号计算两大部分,数值计算是MATLAB的核心,在数值计算过程中,同样需要辅以适当的符号运算,目前符号运算的发展也非常完善,同样得到广泛的应用

MATLAB编程与应用系列-第2章 数组及矩阵的创建及操作(2)

本系列教程来源于出版设计<基于MATLAB编程基础与典型应用书籍>,如涉及版权问题,请联系:[email protected]. 出版社:人民邮电出版社, 页数:525. 本系列教程目前基于MATLABR2006a,可能对于更高级版本的功能和函数有差异,教程中如有问题,请联系:[email protected] 2.2 数组及矩阵的创建和操作 本节将介绍数组及矩阵的创建和操作,包括数组的输入以及数组元素的操作.由于数组和矩阵在创建和寻访的处理方式类似,因此将两者综合一起说明. ###2.2.1

数据库的创建和操作

创建和操作数据库 1.创建数据库的基本要求 [1]主数据文件:一个数据库有,且只能有一个.扩展名是.mdf. [2]次要数据文件:可以根据需要添加多个,并且可以分布在不同的磁盘上.扩展名.ndf. [3]日志文件:有且至少有一个日志文件,也可以多个.扩展名是.ldf. 2.数据库创建要思考的问题 [1]数据容量:根据需要预估. [2]文件分布:根据容量大小,创建不同次要数据文件.分布存储. 3.基于T-SQL脚本创建数据库实例 1 use master--表示当前我们要在master数据库中操作

Phone List(简单的字典树插入操作)

Phone List Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11655    Accepted Submission(s): 3970 Problem Description Given a list of phone numbers, determine if it is consistent in the sense th

第三百七十六节,Django+Xadmin打造上线标准的在线教育平台—创建用户操作app,在models.py文件生成5张表,用户咨询表、课程评论表、用户收藏表、用户消息表、用户学习表

第三百七十六节,Django+Xadmin打造上线标准的在线教育平台-创建用户操作app,在models.py文件生成5张表,用户咨询表.课程评论表.用户收藏表.用户消息表.用户学习表 创建名称为app_operation的用户操作APP,写数据库操作文件models.py models.py文件 #!/usr/bin/env python # -*- coding:utf-8 -*- from __future__ import unicode_literals from datetime i

简单的如何创建sql server存储过程

学习sql server数据库,sql server存储过程的建立方法是一定要知道的,下面将教您如何建立sql server存储过程,希望对您有所帮助. 在对象资源管理器中,连接到某个数据库引擎实例,再展开该实例. 展开“数据库”.sql server存储过程所属的数据库以及“可编程性”. 右键单击“存储过程”,再单击“新建存储过程”. 在“查询”菜单上,单击“指定模板参数的值”. 在“指定模板参数的值”对话框中,“值”列包含参数的建议值.接受这些值或将其替换为新值,再单击“确定”. 在查询编辑

57-005-3 bootstrap实现一个简单的项目案例--新闻操作(基于jsp无刷新异步操作)

图文版页面:http://note.youdao.com/share/?id=fc2d42d8ce97bea6f3272c02e92a2d23&type=note 资源文件下载:https://yunpan.cn/OcRdu34JqpRdtw 访问密码 67af 3.1 本项目的将按照企业标准开发流程,将程序分成几个层次,同时使用mysql数据库,IDEA开发工具 3.2 本程序主要是对新闻数据(编号.标题.发布日期.内容)的一个维护,具体要求: a. 业务层--增加新闻数据 数据层--新闻表中