三篇文章咱們講解了優先隊列,這節主要講解二叉堆的實現,經過二叉堆來實現優先隊列,廢話很少說,咱們開始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;
}
}
複製代碼