数据结构-堆

在看libevent时看到它用小根堆来管理时间,忽然猛地发现堆的概念有点忘了(上一次看数据结构是两年半之前的一个暑假),所以此处复习一下。
堆是利用完全二叉树来维护的一种数据结构,存取操作时间复杂度在O(1)-O(log n)之间,完全二叉树就是除了最后一层外,其他所有层都是满节点,且最后一层如果缺少节点,缺的部分应该在最右边。
堆分为两种类型,大根堆与小根堆,以大根堆为例,每个节点都小于它的父节点。假设现在随手写一串数字 1 , 2 , 10 , -1, 0 ,8,3
先建立成完全二叉树

要把它初始化为一个大顶堆的话,只需要从最后一个非叶子节点开始,依次做下沉动作。对了,操作可分为上浮跟下沉,就是向上或向下调整到对应的位置。最后一个叶子节点索引号可用总个数/2 得到。因为只要所有的非叶子节点位置正确,那整个堆就是正确的。此处就是10这个节点,下沉动作就是向叶子节点找一个大一点的进行比较,如果大于自己,则进行对调,对调后重复比较动作。所以10就是跟8,3比较,没有比10 大的,不做任何改变。同样2,也是。 到1这个节点,首先由于2<10,并且1<10所以,1和10对调

接着,由于3<8,且1<8,所以1要跟8对调。最终结果就是下面这张图

可以看到,10大于2和8,   2大于-1和0  ,  8大于1和3 满足大根堆的性质。

上浮动作类似,就是一直跟父节点比较,如果自己大于父节点,那就对调位置,对调后继续跟新的父节点比较,重复这个比较动作即可。

很多书上讲堆排序,其实就是做一个大根堆,然后每次取出根节点,然后把最后一个节点放到第一个,再做下沉动作。这样保证取出一个节点后仍然为一个大根堆,接着继续取,继续调整...最终节点个数为1,结束。
 

下面给出一个代码,其中通过>>运算符可以插入一个节点,<<运算符可以每次取根元素并调整,也就是说cout这个对象后会得到一个有序序列。
 

#include<iostream>
using namespace std;

class Big_Heap
{
public:
	void shift_down(int k) //大根堆
	{
		for (int i = 2 * k; i <= size; i *= 2) //i*2 下一次循环时就会指向当前的左孩子节点
		{		
			if (i < size && queue[i] < queue[i + 1]) //如果右孩子较大则让i指向右孩子
				i++;
			if (queue[k] < queue[i])
			{
				std::swap(queue[i], queue[k]);
				k = i;
			}
			else
				break;  // 调整结束
		}
		
	}
	void shift_up(int k)
	{
		for (int i = k/2; i >= 1; i/=2)
		{
			if (queue[i] < queue[k])
			{
				std::swap(queue[i], queue[k]);
				k = i;
			}
		}
	}
	friend ostream & operator << (ostream &,  Big_Heap  &);
	Big_Heap& operator >> (int t)
	{
		queue[++size] = t;
		shift_up(size);
		for (int i = 1; i <= size; i++)
			cout << queue[i] << ' ';
		cout << endl;
		return *this;
	}
	void pop()         //弹出操作 
	{
		int temp = queue[1];
		queue[1] = queue[size];
		queue[size] = temp;
		size--;
		shift_down(1);
	}
	int top() { return queue[1]; }
private:
	int size = 0;
	int queue[1024];
};
ostream & operator << (ostream & out,  Big_Heap  & obj)
{
	const int num = obj.size;//下面不断在pop,会改变size的值,所以要先保存下
	for (int i = 1; i <= num; i++)
	{
		out << obj.top() << " ";  //输出堆顶元素 
		obj.pop();        //弹出堆顶元素 
	}
	return out; 
}

int main()
{
	Big_Heap obj;
	obj >> 1 >> 2 >> 10 >> -1 >> 0 >> 8 >> 3;
	
	cout << obj;
	system("pause");
}

这是大根堆的实现方式,小根堆就只需要改几个符号即可,不再重复贴出代码

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值