三篇文章咱们讲解了优先队列,这节主要讲解二叉堆的实现,经过二叉堆来实现优先队列,废话很少说,咱们开始php
堆做为优先队列的底层结构,使得入队和出队操做时间复杂度都在O(logn)数组
以前咱们学习过二叉树,没有了解过二叉堆,从意义字面上还有点相同之处,都有“二叉”两个字(滑稽)数据结构
像上图这样的树型结构就是二叉堆,也看不出什么区别啊,就是节点元素值比左孩子小比右孩子大嘛,和二分搜索树同样了。仔细一看仍是有点区别的,要想成为二叉堆,必需要知足这样两个条件学习
知足上面两个条件就是一个名副其实的二叉堆了,这里咱们彷佛有扯进了一个新的概念,什么是彻底二叉树,搞不懂,蒙圈了,和满二叉树有区别不?有的,满二叉树必定是一个彻底二叉树,反过来就不是了,感受不是很直观,上图一探究竟this
上图就是一个满二叉树,也就是说除了叶子节点没有左右孩子,其他节点都有左右孩子,这样的二叉树就是满二叉树spa
这个树不必定是满的,可能这个节点没有右孩子,也就是元素一层一层的码放,每一层码放满了后在码放下一层,不满的那一部始终是在这个节点的右侧。设计
在现实世界中满二叉树的状况仍是不多的,更多的状况是像彻底二叉树这样,总有那么几个元素孤零零在最后一层,相对来讲彻底二叉树更适用一些,3d
咱们再来看一下这张图,彻底二叉树的叶子节点可能都在最下层,也可能在最下层的上一层,图中就是这样的状况code
二叉堆有最大堆、最小堆,文章一开始图中就有这俩兄弟,他们惟一的区别在于,父亲节点和孩子节点的关系。最大堆是孩子节点不大于其父亲节点的值,最小堆是孩子节点不小于父亲节点cdn
这里的说的最大是根节点最大,最小是根节点最小,以此类推这个节点都大于他的孩子节点的值或小于。这就是特色一,要么是最大堆,要么是最小堆
在说第二个特色前咱们思考一个问题,节点元素的大小与节点所处的层级有关系吗? 从图中看有练习,在上层也就是靠近根节点的元素大于底层元素,结果是这样吗,仔细发现不是这样,从图中能够看到16
比19
层级要高,可是16
的值却比19
要小,因此咱们获得一个结论就是节点元素的大小与节点所处的层级无关
可是有一点是能够肯定的,父亲节点的值要大于孩子节点(这里咱们仅仅谈最大堆)
二叉堆用二叉树实现是能够的,可是有没有能够使用其余数据结构来实现呢, 数组能够嘛?
咱们说过二叉树中的元素是一层一层的码放的,一层满了再放下一层,而数组的底层设计是开辟一块连续的空间,依次存放数据。有那么一点相同之处,咱们大胆的猜想是能够这么作的。
用数组如何表示树的左右孩子呢,有没有规律呢,能够用索引把这种规律给实现了,实际上是有规律的,咱们给每一个节点表上号
把二叉堆按循序存放在一个数组中
我能够发现这样的一个规律,当前这个节点的左孩子索引为index*2
,右孩子为index*2+1
,不行咱们能够试试,索引为4
这节点安装前面的公式获得他的左孩子为8
右孩子为9
,相应的父亲节点索引的公式为index/2
取整
经过这么一个规律咱们就用数组来实现二叉树,不过这个数组有点怪,索引为0
并无存放元素,不只浪费空间并且数组的索引都是从0
开始,因此不得不改造一下,让0
这个地方存放根节点
和上面同样把数据存放到一个数组中
就获得一个索引从0
开始的数组了,大功告成,等等~,上面的公式还能够用嘛,一一得一,一二得二,掐指一算,糟了,不能用了,前面的推到重来
公式来了,这长这样的,有点不同,毕竟开始索引变为了0
相比用二叉树实现二叉堆,这里咱们用数组实现的二叉堆能够根据这个节点找到它的父亲节点,以前我实现的二分搜索树没有这个功能,那是否是说用数组实现二叉堆要好一些呢,这个问题咱们后面谈到
贴代码
namespace heap;
/** * 最大堆 * @package heap */
class MaxHeap {
/** * 堆元素 * @var array */
private $data;
/** * MaxHeap constructor. */
public function __construct() {
$this->data = [];
}
public function getSize() {
return count($this->data);
}
public function isEmpty() {
return count($this->data) == 0;
}
/**返回一个彻底二叉树的数组表示中,一个索引表示的元素的父亲节点的索引 * @param $index * @return int */
private function parent($index) {
if ($index == 0)
throw new \InvalidArgumentException("index-0 does't have parent");
return floor(($index - 1) / 2);
}
/**返回一个彻底二叉树的数组表示中,一个索引表示的元素的左孩子的索引 * @param $index * @return int */
private function leftChild($index) {
return $index * 2 + 1;
}
/**返回一个彻底二叉树的数组表示中,一个索引表示的元素的右孩子的索引 * @param $index * @return int */
private function rightChild($index) {
return $index * 2 + 2;
}
}
复制代码