简单堆的创建和操作

原创 2016年05月31日 09:07:55

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

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

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

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


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


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


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

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


wKiom1cz8uXTjLLRAAAg8X0_uJI833.png

spacer.gif

首先我们需要一个数组将这个二叉树存储起来,因为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;
};


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


wKioL1cz-BOxm6-WAAAgrg5NnEk711.png



那么建立好堆之后,在对数据进行操作的时候对堆也有一定的影响,所以下面我们来简单写一下堆的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;
}

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





本文出自 “福大馨” 博客,转载请与作者联系!

版权声明:本文为博主原创文章,未经博主允许不得转载。

虚拟网卡创建

安装uml-utilities。 Sudo apt-get install uml-utilities 安装完成后,执行命令tunctl -b即可完成网卡的创建,ifconfig tap10 up激活...
  • zxh821112
  • zxh821112
  • 2017年10月24日 14:18
  • 77

sql 表的创建等操作

我们在使用数据库时,大部分都是创建表,然后添加数据,对数据进行
  • u014480364
  • u014480364
  • 2014年04月13日 00:09
  • 370

DOM--如何添加、移除、移动、复制、创建和查找节点等。

创建新节点:createDocumentFragment() //创建一个DOM片段createElement() //创建一个具体的元素createTextNode() //创建一个文...
  • qq_34122603
  • qq_34122603
  • 2017年08月06日 08:31
  • 459

堆的建立和操作

首先引进一个概念:优先队列(Priority Queue):特殊的“队列”,取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。 可以考虑利用一般的数组、链表,有序数组、链表...
  • lafengxiaoyu
  • lafengxiaoyu
  • 2016年11月28日 21:30
  • 476

简单二叉树的建立和遍历

C语言建立先序二叉树,然后分别用先序,中序,后序遍历输出该二叉树。
  • yanxiuhao
  • yanxiuhao
  • 2017年03月13日 13:47
  • 709

第四次作业 6-1 顺序表创建和就地逆置

6-1 顺序表创建和就地逆置(10 分) 本题要求实现顺序表的创建和就地逆置操作函数。L是一个顺序表,函数ListCreate_Sq(SqList &L)用于创建一个顺序表,函数List...
  • starrynightbeats
  • starrynightbeats
  • 2017年09月17日 17:41
  • 967

java实现二叉树创建和遍历

今天晚上我学习了java语言的二叉树创建和遍历算法。 实现的机制和上一节课差不多。 代码如下: package 二叉树链表; public class Node { private in...
  • jjfly999
  • jjfly999
  • 2016年03月08日 21:34
  • 753

操作系统进程线程总结

今天阿里的面试官问了一些关于多线程的问题,感觉自己对这一方面不太了解,于是便google了一些文章,然后又复习了之前学习的操作系统的基础知识,在此进行一下总结,下一篇进行一下线程相关类的源码分析。  ...
  • u012422440
  • u012422440
  • 2015年03月17日 18:17
  • 703

【C】函数的调用过程,栈帧的创建和销毁

每一次函数调用都是一个过程,这个过程我们称之为:函数的调用过程。 函数调用过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存、现场保护。这块栈空间我们称之为函数栈帧。 栈帧的维护需要我们了解e...
  • qq_37924645
  • qq_37924645
  • 2017年04月23日 16:33
  • 150

Android SQLite数据库创建和使用实战(一)

转载地址:http://blog.csdn.net/yanbober/article/details/20688273 Android SQLiteSQLiteOpenHelper数据库...
  • qq_20816947
  • qq_20816947
  • 2015年08月06日 11:05
  • 301
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:简单堆的创建和操作
举报原因:
原因补充:

(最多只允许输入30个字)