贡献者: 有机物
堆是一棵完全二叉树,且每个结点上的编号都不小于或不大于其父结点的编号,堆的结点上并没有权值。而二叉堆的结点上具有权值,且满足堆的性质。满足父结点不小于左右两个子结点的二叉堆被称为大根堆,反之为小根堆。大根堆与小根堆都被属于二叉堆。但是一般没有特别指出的堆一般都指二叉堆。
以一个例题来讲解堆。
维护一个集合,初始时集合为空,支持如下几种操作:
本题要我们实现一个小根堆,根结点是整课树中最小的结点,父结点永远小于等于左右两个子结点。
C++ STL 中的堆只能实现前
要想实现随机删除和修改,只能用数组来模拟堆,所以我们讲解一下如何使用数组模拟堆。
首先需要一个数组 h[N]
用于存储堆中的元素,由于需要在任意位置进行删除和修改,所以需要多开两个数组 ph[N]
和 hp[N]
。
ph[i]
的意思是第 hp[i]
的意思是在堆中下标是 ph[1] = a
,hp[a] = 1
。
因为是随机插入和删除,所以在堆中删除或者插入某个值的话必定要进行调整。分为 up 操作和
堆的存储只用开一个一维数组 h[N]
就可以了,父结点是 u << 1
,u << 1 | 1
。比如父结点的下标是
插入一个数,就直接在堆的结尾插入一个数就可以了,然后再 up 一遍。
输出最小值直接输出堆顶,一定是最小值。
删除最小值直接删除堆顶不太好删,我们可以把堆的结尾的数与堆顶的数交换,再删除堆的结尾的数(交换完之后堆的结尾的数就是堆顶,即最小值)删除,然后再从根节点 down 一遍,这样就实现了删除最小值。
删除任意一个数与删除最小值类似,都是把堆的结尾的数与要删除的数交换,然后再删除堆的结尾的数,最后要注意进行 up 或 down 操作,为了不判断,干脆两个操作都写上,虽然两个函数都执行了,但其中只有一个函数对堆有影响。
修改任意一个数直接修改就好了,最后也要注意进行 up 或 down 操作。
up 操作与 down 操作的时间复杂度都为
因为有任意删除和修改操作,涉及到第 ph[N]
和 hp[N]
。
具体看一下代码:
这三行的代码的意思是:先交换一下 hp
数组,再交换一下对应的 ph
数组,最后再交换堆中的元素。
举个例子:
第一个插入的数是
hp[a] = 1
、hp[b] = 2
、ph[hp[a]] = ph[1] = a
、ph[hp[b]] = ph[2] = b
、h[1] = 3
、h[2] = 4
。
先交换一下 hp
数组就有:hp[a] = 2
,hp[b] = 1
。
再交换一下对应的:ph
数组就有:ph[hp[a]] = ph[2] = a
,ph[hp[b]] = ph[1] = b
。
最后再交换一下对应的 h
数组:h[1] = 4
,h[2] = 3
。
具体的可以看一下图片:
看一下 down 操作:
up 操作:
以上就是堆的基本操作了。
友情链接: 超理论坛 | ©小时科技 保留一切权利