STL源码剖析----heap

本文详细解析了heap数据结构,包括binary heap的概念,最大堆和最小堆的区别,以及STL中heap相关算法如push_heap、pop_heap、sort_heap、make_heap的工作原理。通过源码分析,帮助读者深入理解heap在C++中的实现。

heap就是一个基础容器+heap算法,array和vector都可以作为heap的基础容器,vector可以动态改变容量,比array会更好。
heap并不归属STL容器,它只是在priority queue容器里起辅助作用,priority queue把heap作为底层机制,可以更好地完成它接口里的一些操作。
binary heap就是一种完全二叉树,就是除了最底层的叶子节点外,都是填满的,且最底层叶子节点是由左到右依次填充的。
heap可分为最大堆和最小堆,最大堆就是每个父子节点都大于或等于其子节点,根节点就是堆中最大值。最小堆就是每个父子节点都小于或等于其子节点,根节点就是堆中最小的值。STL供应的是最大堆,下面的heap都是最大堆。

push_heap算法:

因为要满足完全二叉树条件,加入新元素的位置所以必须是树的最底层,且从左到右的第一个空位,也就是vector的end()位置。
push_heap算法就在新元素加入后调整heap,把新元素调到合适位置。将新元素和父节点比较,如果比父节点值大,就交换父子节点的位置,再把父节点当做新的子节点,再往上调,以此类推。直到父子节点不用调换或到根节点。
下面就push_heap算法源码,用push_heap算法必须符合两个条件:
1、函数接收两个迭代器,用来表现heap底部容器(vector)的头和尾。
2、新元素已经插入底部容器的尾端,push_heap算法只是调整,不包含新元素插入。
在这里插入图片描述

template <class RandomAccessIterator>
inline void push_heap(RandomAccessIterator first, RandomAccessIterator last) {
	__push_heap_aux(first, last, distance_type(first), value_type(first));
}
template <class RandomAccessIterator, class Distance, class T>
inline void __push_heap_aux(RandomAccessIterator first,
	RandomAccessIterator last, Distance*, T*) {
	//在__push_heap中都是以元素和first的距离表示元素下标,叫洞号,即Distance(Iterator-first),
	//Distance((last - first) - 1)就是最后元素的洞号,也是要开始调整的洞号,
	//Distance(0)根节点洞号
	//T(*(last - 1))最后元素的值
	__push_heap(first, Distance((last - first) - 1), Distance(0),
		T(*(last - 1)));
}
template <class RandomAccessIterator, class Distance, class T>
void __push_heap(RandomAccessIterator first, Distance holeIndex,
	Distance topIndex, T value) {
	 //当前节点父节点洞号
	Distance parent = (holeIndex - 1) / 2;
	//把value当做比较值,小于value的节点都往下调
	//当前节点还不是根节点,且父节点值小于value值时,把父节点值赋给子节点值,再把父节点当新子节点一直循环
	while (holeIndex > topIndex && *(first + parent) < value) {
		*(first + holeIndex) = *(first + parent);
		holeIndex = parent;
		parent = (holeIndex - 1) / 2;
	}
	//最后holeIndex洞号节点要么是根节点,要么是value小于其父节点值,
	//因为在while里已经把小于value的节点都往下调,所以holeIndex就是value的位置
	*(first + holeIndex) = value;
}
pop_heap算法:

pop_heap算法就是把max_heap的最大值,也就是根节点pop出去(实际就是把根节点放到vector的尾部),
第一步先把尾节点的值value保存下来。
第二步把根节点的值,也就是最大值赋给尾节点。
第三步执行percolate down(下溯)流程,就是把较大的子节点赋给父节点(起始父节点是根节点),往上放,直到叶子节点。
第四步执行_push_heap函数,上溯,把父节点小于value的值往下放,找到value的位置
源码:
在这里插入图片描述

template <class RandomAccessIterator>
inline void pop_heap(RandomAccessIterator first, RandomAccessIterator last) {
	__pop_heap_aux(first, last, value_type(first));
}
template <class RandomAccessIterator, class T>
inline void __pop_heap_aux(RandomAccessIterator first,
	RandomAccessIterator last, T*) {
	//最后一个节点的值T(*(last - 1))传给value,保存下来
	__pop_heap(first, last - 1, last - 1, T(*(last - 1)), distance_type(first));
}
template <class RandomAccessIterator, class T, class Distance>
inline void __pop_heap(RandomAccessIterator first, RandomAccessIterator last,
	RandomAccessIterator result, T value, Distance*) {
	//把根节点的值赋给尾节点
	*result = *first;
	//Distance(last - first)已经是heap原来长度减1,因为这里的last是指向最后一个元素
	__adjust_heap(first, Distance(0), Distance(last - first), value);
}
template <class RandomAccessIterator, class Distance, class T>
void __adjust_heap(RandomAccessIterator first, Distance holeIndex,
	Distance len, T value) {
	Distance topIndex = holeIndex;
	//secondChild 每次都赋为节点右孩子
	Distance secondChild = 2 * holeIndex + 2;
	//下溯
	//当右孩子没超出范围,即该节点还有右孩子
	while (secondChild < len) {
		//比较左右孩子,把secondChild定为较大的孩子
		if (*(first + secondChild) < *(first + (secondChild - 1)))
			secondChild--;
		//把secondChild孩子的值赋给父节点,即把孩子往上放
		*(first + holeIndex) = *(first + secondChild);
		//把secondChild孩子当做新的父节点,再取其右孩子,循环下去
		holeIndex = secondChild;
		secondChild = 2 * (secondChild + 1);
	}
	//若最后循环到的节点没有右孩子,
	if (secondChild == len) {
		//把左孩子值赋给洞值(就是当前父节点)
		*(first + holeIndex) = *(first + (secondChild - 1));
		//令左孩子为洞号
		holeIndex = secondChild - 1;
	}
	//上溯,
	__push_heap(first, holeIndex, topIndex, value);
}
sort_heap算法:

pop_heap会把最大值放到底部容器的尾端,如果对heap一直pop_heap(每pop_heap一次,把操作范围减1),直到操作范围只剩下一个元素,那么就会得到一个有序的序列,sort_heap就是这样操作的,
在这里插入图片描述
在这里插入图片描述

template <class RandomAccessIterator>
void sort_heap(RandomAccessIterator first, RandomAccessIterator last) {
	while (last - first > 1) pop_heap(first, last--);
}
make_heap算法:

make_heap是将一段数据转化为一个heap,

template <class RandomAccessIterator>
inline void make_heap(RandomAccessIterator first, RandomAccessIterator last) {
	__make_heap(first, last, value_type(first), distance_type(first));
}
template <class RandomAccessIterator, class T, class Distance>
void __make_heap(RandomAccessIterator first, RandomAccessIterator last, T*,
	Distance*) {
	//若长度小于2,不需要重新排列
	if (last - first < 2) return;
	//数据段的长度
	Distance len = last - first;
	//最后一个节点的父节点
	Distance parent = (len - 2) / 2;

	while (true) {
		//
		__adjust_heap(first, parent, len, T(*(first + parent)));
		if (parent == 0) return;
		parent--;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值