使用C++实现二叉堆

二叉堆可以用来实现优先队列。二叉堆是一棵完全二叉树,除了叶子节点那一层,其他的层都是满的,叶子节点那一层是从左往右依次存放的,因此它们的父子元素的索引有数学公式可以参考,对于节点i来说,它的左儿子是2i,右儿子是2i+1,它的父亲节点是i/2向下取整,因此,在其内部我们可以通过数组来实现元素的存储,从而不需要链式结构,可以节约存储空间,提高访问速度(但是如果有合并操作,则会很慢,此时使用链式结构是不错的选择,可以采用左式堆或者斜堆以及二项队列)。对于二叉堆来说,它具有堆序特性,及父节点小于(小顶堆)或者大于(大顶堆(STL中的优先队列默认是大顶堆))儿子节点。


下面记录一下今天的代码:

首先是二叉堆的实现模板

#ifndef MY_BINARY_HEAP_H
#define MY_BINARY_HEAP_H
#include<vector>
#include<iostream>
using namespace std;

/*
*	实现小顶堆
*/
template<typename Comparable>
class MyBinaryHeap
{
public:
	explicit MyBinaryHeap(int Capacity = 100) 
		: array(Capacity + 1), currentSize(0){}
	explicit MyBinaryHeap(const vector<Comparable> & items) 
		: array(items.size() + 10), currentSize(items.size())
	{
		for (unsigned int i = 0; i < items.size(); i++)
		{
			array[i + 1] = items[i];
		}
		buildHeap();
	}
	explicit MyBinaryHeap(const MyBinaryHeap<Comparable> & rhs) 
		: array(rhs.array), currentSize(rhs.currentSize){}

	bool isEmpty(){ return 0 == currentSize; }
	const Comparable & findMin() const{ return array[1]; }
	
	int getSize() { return currentSize; }
	void insert(const Comparable & x);
	void deleteMin();
	void deleteMin(Comparable & minItem);
	void makeEmpty(){
		currentSize = 0;
	}

private:
	int currentSize; //存放当前heap中的元素
	vector<Comparable> array; //数组第一个元素,即index == 0 的位置上没有元素
	                          //从index==1的位置开始放置元素,这样方便对数组下标和元素位置进行统一操作

	void buildHeap();
	void percolateDown(int hole);
};


/*
* 建立堆序
* 时间为O(N)
*/
template<typename Comparable>
void MyBinaryHeap<Comparable>::buildHeap()
{
	for (int i = currentSize / 2; i > 0; i--)
		percolateDown(i);
}

/*
* 在二叉堆中插入一个元素
* 首先在放在最后的位置上,然后不断通过上滤(percolate up)算法
* 一直找到比它小的节点,此时插入的元素成为这个节点的子树
* 如果到达根部 hole==1,且根比它小,则它就成为根
* 插入的时间为 O(logN)
*/
template<typename Comparable>
void MyBinaryHeap<Comparable>::insert(const Comparable & x)
{
	if (currentSize == array.size() - 1)
		array.resize(array.size() * 2);

	int hole = ++currentSize;
	for (; hole > 1 && x < array[hole / 2]; hole /= 2)
	{
		array[hole] = array[hole / 2];
	}
	array[hole] = x;
}

/*
* 删除最小项
* 如果本来二叉堆就是空的,执行删除最小的元素这个操作时,在标准错误输出上打印,并停机
*/
template<typename Comparable>
void MyBinaryHeap<Comparable>::deleteMin()
{
	if (isEmpty())
	{
		cerr << "此时堆内为空,不能删除最小元素" << endl;
		exit(1);
	}
	array[1] = array[currentSize--];
	percolateDown(1);

}

/*
* 删除最小项,并且把最小值放在minItem中
* 如果本来二叉堆就是空的,执行删除最小的元素这个操作时,在标准错误输出上打印,并停机
*/
template<typename Comparable>
void MyBinaryHeap<Comparable>::deleteMin(Comparable & minItem)
{
	if (isEmpty())
	{
		cerr << "此时堆内为空,不能删除最小元素" << endl;
		exit(1);
	}
	minItem = array[1];
	array[1] = array[currentSize--];
	percolateDown(1);
}

/*
* 这个私有的方法主要用来实现下滤的算法(percolate down)
* 参数hole指示了下滤开始的节点
* 平均时间O(logN)
*/
template<typename Comparable>
void MyBinaryHeap<Comparable>::percolateDown(int hole)
{
	int child;
	Comparable tmp = array[hole];
	for (; hole * 2 <= currentSize; hole = child)
	{
		child = hole * 2; // 尝试对左儿子进行比较
		if (child != currentSize && array[child + 1] < array[child])
			child++; // 如果右儿子比左儿子小,则尝试对右儿子进行比较
		if (array[child] < tmp) // 如果下滤下来的这个点比两个儿子中最小的大,最小的成为此时子树的根
			array[hole] = array[child];
		else
			break;
	}
	array[hole] = tmp;
}

#endif

然后是测试代码:

#include"MyBinaryHeap.h"
#include<vector>
#include<random>
#include<ctime>
#include<iostream>
using namespace std;

int main()
{
	
	vector<int> tmpVec;
	MyBinaryHeap<int> BH1;//使用无参的构造函数
	default_random_engine e(static_cast<unsigned int>(time(0)));
	uniform_int_distribution<int> u(0,100);

	cout << "测试插入数据:" << endl;
	cout << "==== 原始数据为:";
	for (int i = 0; i < 15; ++i)
	{
		int tmp = u(e);
		cout << tmp << ' ';
		BH1.insert(tmp);
		tmpVec.push_back(tmp);
	}
	MyBinaryHeap<int> BH2(tmpVec);//通过数组来构造
	cout << "此时有:" << BH1.getSize() <<"个元素,其中最小值为:" << BH1.findMin() << endl;
	cout << "测试拷贝构造函数:" << endl;
	MyBinaryHeap<int> BH3(BH1);//拷贝构造函数

	cout << "==== 通过取最小值,获得从小到大的排序 =====" << endl;
	cout << "BH1:" << endl;
	while (!BH1.isEmpty())
	{
		int tmp = 0;
		BH1.deleteMin(tmp);
		cout << tmp << " ";
	}
	cout << endl;
	cout << "BH2:" << endl;
	while (!BH2.isEmpty())
	{
		int tmp = 0;
		BH2.deleteMin(tmp);
		cout << tmp << " ";
	}
	cout << endl;
	cout << "BH3:" << endl;
	while (!BH3.isEmpty())
	{
		int tmp = 0;
		BH3.deleteMin(tmp);
		cout << tmp << " ";
	}
	cout << endl;

	return 0;
}

测试的结果为:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值