数据结构之最大堆

简介

最大堆是一种很常见的数据结构;下面我们先了解一下什么是堆;

堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称最大堆;或者每个节点的值都小于或者等于其左右孩子节点的值,称最小堆;下图左边是最大堆,右边是最小堆;我们这里以最大堆进行说明,最小堆和最大堆类似。
在这里插入图片描述

程序

1. 前期准备

堆是一种完全二叉树,而完全二叉树常见的表达方式是树的结构或者数组。这里我们采用数组的形式来描述最大堆;

class heap {
public:
	heap() : n(0) , nums(vector<int>()){	}
	heap(initializer_list<int> datas) {
		for (auto data : datas)
			push(data);
	}
	void push(int data);
	int top();
	void pop();
	int size() {
		return n;
	}
private:
	void adjust(int index);
	vector<int> nums;
	int n;
};

nums保存堆的数据,n记录堆中元素的个数; 最大堆常见的操作有3个:插入(push),取出最大值(top)以及删除最大值(pop)。

2. 插入函数【从下往上】

思路:将新的值放到数组最后面,然后与它的头节点进行比较;如果头节点的值比它小,就交换数据,并继续和新的头节点比较;如果头节点的值比它大,则说明现在已经满足最大堆的条件

void heap::push(int data) {
	++n;
	nums.push_back(data);
	int i;
	//i 是子节点的下标,(i - 1) / 2 是子节点的父节点的下标
	for (i = n - 1; i > 0 && data > nums[(i - 1) / 2]; i = (i - 1) / 2) {
		nums[i] = nums[(i - 1) / 2];
	}
	nums[i] = data;
}

分析:
1.这里的难点是弄清楚头节点的下标对于下标从1开始的数组,左节点 = 头节点 * 2;对于下标从0开始的数组,左节点 = 头节点 * 2 + 1;

2.一定要注意 i > 0 这个条件;

3. 取出最大值函数

思路:堆顶的值就是最大值,直接取出即可

int heap::top() {
	if (n == 0) {
		cout << "没有值,无法返回" << endl;
		return INT_MIN;
	}
	return nums[0];
}

4. 删除最大值【从下往上】

思路:交换第一个值和最后一个值,从头节点开始调整

void heap::pop() {
	if (n == 0) {
		cout << "没有值,无法删除" << endl;
		return;
	}
	swap(nums[0], nums[n - 1]);
	--n;	
	adjust(0);
}

void heap::adjust(int index) {
	int left;
	for (; index * 2 + 1 < n; index = left) {
		//left 是左节点
		left = index * 2 + 1;

		//判断右节点是否存在,如果存在判断右节点和左节点谁大
		if (left + 1 < n && nums[left] < nums[left + 1])
			left++;
		if (nums[index] < nums[left])
			swap(nums[index], nums[left]);
	}
}

分析:

  1. index 是父节点的下标,左节点的下标为 index * 2 + 1,右节点的下标为 index * 2 + 2
  2. 一定要判断右节点是否存在,因为 for 循环的条件是左节点存在

总结

  1. 这里的 pop() 并没有将数据从 nums 中删除,仅仅是缩小了 n
  2. 因为构造函数中使用了 initializer_list 所以可以使用花括号的初始化;
heap h{10 ,1, 4 , 6 , 2};
int n = h.size();
for (int i = 0; i < n; ++i) {
	cout << h.top() << " ";
	h.pop();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值