堆类模板-C++实现

什么是堆

是一种重要的数据类型,是一种特殊的完全二叉树。
堆的递归定义为:
1)堆可以是一棵空树。
2)如果堆不为空,则堆的根结点比所有子孙结点大(或小)。
3)堆的子树也是堆。
根据堆的定义,可以得出:从堆的根结点沿任意路径到叶节点,经过的结点具有有序性。
例:如下图所示的一棵二叉树
在这里插入图片描述
从树的根结点到叶节点的全体路径为:
10 7 5
10 7 1
10 2
全体路径都是从大到小的序列,并且该二叉树是完全二叉树,故该二叉树是堆。
像这样沿任意路径,结点递降的堆,叫做最大堆(或大根堆);同理,沿任意路径,结点递增的堆,叫做最小堆(或小根堆)。

堆的基本操作:最大堆为例

堆是一棵完全二叉树,所以可以用数组实现堆。

template<class T, int MaxSize>	//T是堆中存放的数据类型,MaxSize是堆的最大容量
class MaxHeap {
private:
   T buffer[MaxSize];	//存放堆的数组
   int _size;	//堆的当前数据量
   static T out_of_range;	//试图访问空堆时,返回越界数据
public:
   MaxHeap();	//构造函数
   void insert(T const&insert_element);	//插入操作
   T pop_max();	//弹出最大数据:根结点对应的数据
   void clear();	//清空堆
   bool empty()const;	//判断堆是否为空
   bool full()const;	//判断堆是否为满
   int size()const;	//返回堆的当前数据线
   int max_size()const;	//返回堆的最大容量
};

最大堆的插入:向上调整

最大堆的插入分两步进行:
1)插入:和完全二叉树一样,直接把数据插入到堆的最后一个结点末尾。
2)有序性调整:前文提到,从堆的根结点沿任意路径到叶节点,经过的结点具有有序性。但是插入的新结点可能破坏堆的有序性,所以需要从下向上调整。
具体的调整方法为:从插入的结点开始,依次和父结点比较,如果父结点较小,就交换父结点与插入结点,直到插入结点不大于父结点为止。

template<class T, int MaxSize>
void MaxHeap<T, MaxSize>::insert(T const&insert_element) {
	if (full())return;	//如果堆满,则不进行插入
	buffer[_size] = insert_element;	//在堆的最后一个结点末尾插入数据
	if (!_size++)return;	//如果堆为空,则可以直接返回
	int pos = _size - 1, father;	//pos记录插入结点下标,father记录父结点下标
	while (pos) {	//当插入结点调整为根结点时结束循环
		father = (pos - 1) / 2;	//由完全二叉树的性质,父结点的下标由左式给出
		if (buffer[father] < buffer[pos]) {	//如果父结点较小,就交换父结点与插入结点
			auto temp = buffer[father];
			buffer[father] = buffer[pos];
			buffer[pos] = temp;
			pos = father;
		}
		else break;	//插入结点不大于父结点,有序性调整结束
	}
}

弹出最大数据:向下调整

由最大堆的定义,最大数据在根结点上,所以需要删除根结点,并返回根结点的数据。
最大堆的删除分三步进行:
1)交换:交换根结点与最后一个结点的位置,方便删除操作。
2)删除:由于根结点与最后一个结点换位,只需要删除最后一个结点。
2)有序性调整:最后一个结点被换到根结点上,很可能破坏堆的有序性,所以需要从上向下调整。
具体的调整方法为:从跟结点开始,依次和较大的子结点比较,如果父结点较小,就交换父结点与较大子结点,直到根结点不小于任意子结点为止。

template<class T, int MaxSize>
T MaxHeap<T, MaxSize>::pop_max() {
	if (empty())return out_of_range;	//如果堆为空,则直接返回
	auto output = *buffer;	//因为要删除根结点,先记录下根结点的数据
	*buffer = buffer[_size-- - 1];	//交换根结点与最后一个结点,并删除最后一个结点
	if (1 == _size)return output;	//如果堆只剩下一个结点,则直接返回原根结点数据
	int pos = 0, l_child, r_child;	//pos记录根结点下标,l_child和r_child分别记录左右子结点下标
	while (2 * pos + 1 < _size) {	//当根结点被调整到叶节点上时,结束有序性调整
		l_child = 2 * pos + 1;
		r_child = 2 * pos + 2;	//由完全二叉树的性质,子结点的下标由左式给出
		if (2 * pos + 2 == _size)
			if (buffer[l_child] > buffer[pos]) {
				auto temp = buffer[l_child];
				buffer[l_child] = buffer[pos];
				buffer[pos] = temp;
				pos = l_child;
			}
			else break;
		else if (buffer[l_child] > buffer[r_child])	//选择较大的子结点,与根结点比较
			if (buffer[l_child] > buffer[pos]) {	//如果根结点较小,则交换根结点与该子结点
				auto temp = buffer[l_child];
				buffer[l_child] = buffer[pos];
				buffer[pos] = temp;
				pos = l_child;
			}
			else break;	//根结点不小于任意子结点,有序性调整结束
		else if (buffer[r_child] > buffer[pos]) {
			auto temp = buffer[r_child];
			buffer[r_child] = buffer[pos];
			buffer[pos] = temp;
			pos = r_child;
		}
		else break;
	}
	return output;	//返回原根结点的数据
}

最小堆的操作与最大堆类似,本文不做赘述。

使用堆的注意事项

如果堆储存的是用户定义的数据类型,则需要重载关系运算符,或者指定一个键值作为比较数据大小的基准。
例:用最小堆实现哈夫曼编码。
构造哈夫曼树的依据是结点的权值,所以应该指定结点的权值为键值。

完整代码

//堆:数组实现
#ifndef OCHEAP_H
#define OCHEAP_H

#ifndef _OC_BEGIN
#define _OC_BEGIN namespace OC{
#endif

#ifndef _OC_END
#define _OC_END }
#endif

#ifndef OCHEAP_DEBUG
#define OCHEAP_DEBUG 0
#include<iostream>
using std::cout;
using std::endl;
#endif

_OC_BEGIN

//_MAXHEAP_BEGIN

template<class T, int MaxSize>
class MaxHeap {
private:
	T buffer[MaxSize];
	int _size;
	static T out_of_range;
public:
	MaxHeap();
	void insert(T const&insert_element);
	T pop_max();
	void clear();
	bool empty()const;
	bool full()const;
	int size()const;
	int max_size()const;
#if OCHEAP_DEBUG
	void LevleOrderTraversal()const {
		auto it = buffer;
		for (int count = 0; count != _size; count++)
			cout << *it++ << ' ';
	}
#endif
};

template<class T, int MaxSize>
T MaxHeap<T, MaxSize>::out_of_range;

template<class T, int MaxSize>
MaxHeap<T, MaxSize>::MaxHeap() :_size(0) {

}

template<class T, int MaxSize>
void MaxHeap<T, MaxSize>::insert(T const&insert_element) {
	if (full())return;
	buffer[_size] = insert_element;
	if (!_size++)return;
	int pos = _size - 1, father;
	while (pos) {
		father = (pos - 1) / 2;
		if (buffer[father] < buffer[pos]) {
			auto temp = buffer[father];
			buffer[father] = buffer[pos];
			buffer[pos] = temp;
			pos = father;
		}
		else break;
	}
}

template<class T, int MaxSize>
T MaxHeap<T, MaxSize>::pop_max() {
	if (empty())return out_of_range;
	auto output = *buffer;
	*buffer = buffer[_size-- - 1];
	if (1 == _size)return output;
	int pos = 0, l_child, r_child;
	while (2 * pos + 2 < _size) {
		l_child = 2 * pos + 1;
		r_child = 2 * pos + 2;
		if (buffer[l_child] > buffer[r_child])
			if (buffer[l_child] > buffer[pos]) {
				auto temp = buffer[l_child];
				buffer[l_child] = buffer[pos];
				buffer[pos] = temp;
				pos = l_child;
			}
			else break;
		else if (buffer[r_child] > buffer[pos]) {
			auto temp = buffer[r_child];
			buffer[r_child] = buffer[pos];
			buffer[pos] = temp;
			pos = r_child;
		}
		else break;
	}
	return output;
}

template<class T, int MaxSize>
void MaxHeap<T, MaxSize>::clear() {
	_size = 0;
}

template<class T, int MaxSize>
bool MaxHeap<T, MaxSize>::empty()const {
	return _size ? false : true;
}

template<class T, int MaxSize>
bool MaxHeap<T, MaxSize>::full()const {
	return _size == MaxSize ? true : false;
}

template<class T, int MaxSize>
int MaxHeap<T, MaxSize>::size()const {
	return _size;
}

template<class T, int MaxSize>
int MaxHeap<T, MaxSize>::max_size()const {
	return MaxSize;
}

//_MAXHEAP_END

//_MINHEAP_BEGIN

template<class T, int MaxSize>
class MinHeap {
private:
	T buffer[MaxSize];
	int _size;
	static T out_of_range;
public:
	MinHeap();
	void insert(T const&insert_element);
	T pop_min();
	void clear();
	bool empty()const;
	bool full()const;
	int size()const;
	int max_size()const;
#if OCHEAP_DEBUG
	void LevleOrderTraversal()const {
		auto it = buffer;
		for (int count = 0; count != _size; count++)
			cout << *it++ << ' ';
	}
#endif
};

template<class T, int MaxSize>
T MinHeap<T, MaxSize>::out_of_range;

template<class T, int MaxSize>
MinHeap<T, MaxSize>::MinHeap() :_size(0) {

}

template<class T, int MaxSize>
void MinHeap<T, MaxSize>::insert(T const&insert_element) {
	if (full())return;
	buffer[_size] = insert_element;
	if (!_size++)return;
	int pos = _size - 1, father;
	while (pos) {
		father = (pos - 1) / 2;
		if (buffer[father] > buffer[pos]) {
			auto temp = buffer[father];
			buffer[father] = buffer[pos];
			buffer[pos] = temp;
			pos = father;
		}
		else break;
	}
}

template<class T, int MaxSize>
T MinHeap<T, MaxSize>::pop_min() {
	if (empty())return out_of_range;
	auto output = *buffer;
	*buffer = buffer[_size-- - 1];
	if (1 == _size)return output;
	int pos = 0, l_child, r_child;
	while (2 * pos + 2 < _size) {
		l_child = 2 * pos + 1;
		r_child = 2 * pos + 2;
		if (buffer[l_child] < buffer[r_child])
			if (buffer[l_child] < buffer[pos]) {
				auto temp = buffer[l_child];
				buffer[l_child] = buffer[pos];
				buffer[pos] = temp;
				pos = l_child;
			}
			else break;
		else if (buffer[r_child] < buffer[pos]) {
			auto temp = buffer[r_child];
			buffer[r_child] = buffer[pos];
			buffer[pos] = temp;
			pos = r_child;
		}
		else break;
	}
	return output;
}

template<class T, int MaxSize>
void MinHeap<T, MaxSize>::clear() {
	_size = 0;
}


template<class T, int MaxSize>
bool MinHeap<T, MaxSize>::empty()const {
	return _size ? false : true;
}

template<class T, int MaxSize>
bool MinHeap<T, MaxSize>::full()const {
	return _size == MaxSize ? true : false;
}

template<class T, int MaxSize>
int MinHeap<T, MaxSize>::size()const {
	return _size;
}

template<class T, int MaxSize>
int MinHeap<T, MaxSize>::max_size()const {
	return MaxSize;
}

//_MINHEAP_END

_OC_END

#endif
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是Dijkstra算法的C++类模板实现,可以适用于任何具有权重边的图(有向或无向): ```c++ #include <iostream> #include <vector> #include <queue> #include <limits> template <typename T> class Dijkstra { private: std::vector<std::vector<std::pair<int, T>>> graph; // 存储图的邻接表 std::vector<T> dist; // 存储每个顶点的最短距离 std::vector<int> prev; // 存储每个顶点在最短路径中的前驱结点 const T INF = std::numeric_limits<T>::max(); // 无穷大 public: Dijkstra(const std::vector<std::vector<std::pair<int, T>>>& g) : graph(g) {} void run(int start) { int n = graph.size(); dist.assign(n, INF); prev.assign(n, -1); // 使用小根存储每个顶点的最短距离及其编号 std::priority_queue<std::pair<T, int>, std::vector<std::pair<T, int>>, std::greater<>> pq; pq.emplace(0, start); dist[start] = 0; while (!pq.empty()) { auto [d, u] = pq.top(); pq.pop(); // 如果当前节点已经被处理过,就直接跳过 if (dist[u] < d) { continue; } // 遍历当前节点所有的邻居节点 for (auto [v, w] : graph[u]) { T newDist = dist[u] + w; if (newDist < dist[v]) { dist[v] = newDist; prev[v] = u; pq.emplace(newDist, v); } } } } // 获取从起点到终点的最短距离 T getDistance(int end) const { return dist[end]; } // 获取从起点到终点的最短路径 std::vector<int> getPath(int end) const { std::vector<int> path; for (int u = end; u != -1; u = prev[u]) { path.push_back(u); } std::reverse(path.begin(), path.end()); return path; } }; ``` 使用示例: ```c++ int main() { // 构建有向图 int n = 5; std::vector<std::vector<std::pair<int, int>>> graph(n); graph[0].emplace_back(1, 10); graph[0].emplace_back(3, 5); graph[1].emplace_back(2, 1); graph[1].emplace_back(3, 2); graph[2].emplace_back(4, 4); graph[3].emplace_back(1, 3); graph[3].emplace_back(2, 9); graph[3].emplace_back(4, 2); // 运行Dijkstra算法 Dijkstra<int> dijkstra(graph); dijkstra.run(0); // 输出结果 for (int i = 0; i < n; i++) { std::cout << "Distance from 0 to " << i << ": " << dijkstra.getDistance(i) << std::endl; auto path = dijkstra.getPath(i); std::cout << "Path from 0 to " << i << ": "; for (int u : path) { std::cout << u << " "; } std::cout << std::endl; } return 0; } ``` 输出结果: ``` Distance from 0 to 0: 0 Path from 0 to 0: 0 Distance from 0 to 1: 8 Path from 0 to 1: 0 3 1 Distance from 0 to 2: 9 Path from 0 to 2: 0 3 1 2 Distance from 0 to 3: 5 Path from 0 to 3: 0 3 Distance from 0 to 4: 7 Path from 0 to 4: 0 3 4 ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值