(数据结构)堆的实现

(最大)堆的定义

堆总是一棵完全二叉树;且
最大堆总满足,堆中父节点的值总是大于等于其左右子节点的值;
最小堆总满足,堆中父节点的值总是小于等于其左右子节点的值。

(最大)堆的底层实现

1.数据存储

如下图所示,如果自顶向下,自左向右依次将完全二叉树中的每个节点标上序号(【注】此处从1开始),并使用数组存储(【注】数组中下标0对应的位置未放置元素),则可以发现规律:如果当前节点的序号为i,则其父节点的序号为i/2,其左孩子节点的序号为2*i,其右孩子节点的序号为2*i+1
在这里插入图片描述

2.插入元素时,最大堆的维护(shiftUp)

如图所示,新插入的元素首先放在最后。维护的逻辑为:依次与其父节点比较大小,若大于父节点,则交换二者的值;直到某次比较发现小于等于父节点满足或已经为根节点。
对于图中给出的例子,待比较的节点,已使用绿色圆圈表示。
在这里插入图片描述
直接给出调整之后的结果图:
在这里插入图片描述

3.取出最大值时,最大堆的维护(shiftDown)

如图所示,当从最大堆中取出堆顶(即最大)元素后,维护的逻辑为:首先将最后一个元素放在堆顶位置,然后以堆顶为起点,执行shiftDown。即依次与其左右子节点中较大的一个较大小,若大于父节点,则交换二者的值;直到某次比较发现大于等于左右子节点满足或已经为叶子节点。
对于图中给出的例子,待比较的节点,已使用绿色圆圈表示。
在这里插入图片描述
直接给出调整之后的结果图:
在这里插入图片描述

最大堆的C++实现

#include <iostream>
#include <cassert>
#include <random>

template<typename Item>
class MaxHeap {
private:
	Item* _data;   // 维护一个指向数组的指针!
	int _size;     // 数组中,实际存在的【元素个数】
	int _capacity; // 数组的 最大容积!

	void shiftUp(int k) {
		// 将k位置元素与其父节点元素比较,直到符合不大于的要求,或至根节点
		while (k > 1 && _data[k] > _data[k / 2]) {
			std::swap(_data[k], _data[k / 2]);
			k /= 2;
		}
	}

	void shiftDown(int k) {
		//if (_size == 0) return;
		//if (_size == 1) return;
		while (2 * k <= _size) {    // 【左节点存在】!不是叶节点,可能还需要交换
			int j = 2 * k;      // j表示 待交换的节点;此时表示左节点
			if (j + 1 <= _size && _data[j + 1] > _data[j]) { // 【右节点存在】,且比左节点大!
				j = j + 1;  // 更新待交换节点
			}
			if (_data[k] < _data[j]) { // 当前节点比待交换节点小!
				std::swap(_data[k], _data[j]);
				k = j;
			}
			else break;   // 符合父节点大于左右子节点
		}
		return;
	}
public:
	MaxHeap(int capacity) :_capacity(capacity) {
		_data = new Item[capacity + 1]; // 从1开始保存元素
		_size = 0; // 初始时,容积为capacity,实际元素个数为0.
	}
	~MaxHeap() {
		delete[] _data;
	}
	int size() {
		return _size;
	}
	bool isEmpty() {
		return _size == 0;
	}
	void insert(Item item) {
		// 0 1 2 3 ... _size ... _capacity
		// - # # # ...   #
		assert(_size + 1 <= _capacity);
		// 暂时实现固定容量大小的堆
		_data[_size + 1] = item;
		_size++;
		shiftUp(_size);
	}
	Item extractMax() {
		assert(_size >= 1); // [1, ... _size]保存元素
		Item ret = _data[_size];
		std::swap(_data[1], _data[_size]);
		_size--;
		shiftDown(1);

		return ret;
	}
	void printMaxHeap() {
		std::cout << "maxheap is: ";
		for (int i = 1; i <= _size; ++i) {
			std::cout << _data[i] << " ";
		}
		std::cout << std::endl;
	}
};
int main() {
	MaxHeap<int> maxheap = MaxHeap<int>(100);
	
	std::uniform_int_distribution<unsigned> u(0, 100);
	std::default_random_engine e;
	// 生成0到100之间(包含)均匀分布的随机数
	for (size_t i = 0; i < 10; ++i) {
		maxheap.insert(u(e));
	}
	maxheap.printMaxHeap();
	
	maxheap.extractMax();
	maxheap.printMaxHeap();

	maxheap.extractMax();
	maxheap.printMaxHeap();
	return 0;
}

程序的运行结果:
在这里插入图片描述


此时,堆是基于C++语言,实现(一种抽象数据类型ADT)的一整套算法,是一种数据结构。
ps. 抽象数据类型 VS 数据结构
图
最后类比一下vector的实现,体会一下堆:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值