二叉堆的實現

三篇文章咱們講解了優先隊列,這節主要講解二叉堆的實現,經過二叉堆來實現優先隊列,廢話很少說,咱們開始php

堆做爲優先隊列的底層結構,使得入隊和出隊操做時間複雜度都在O(logn)數組

什麼是二叉堆?

以前咱們學習過二叉樹,沒有了解過二叉堆,從意義字面上還有點相同之處,都有「二叉」兩個字(滑稽)數據結構

像上圖這樣的樹型結構就是二叉堆,也看不出什麼區別啊,就是節點元素值比左孩子小比右孩子大嘛,和二分搜索樹同樣了。仔細一看仍是有點區別的,要想成爲二叉堆,必需要知足這樣兩個條件學習

  • 是一個徹底二叉樹
  • 堆中任意一個節點的值不大於其父親節點的值

知足上面兩個條件就是一個名副其實的二叉堆了,這裏咱們彷佛有扯進了一個新的概念,什麼是徹底二叉樹,搞不懂,蒙圈了,和滿二叉樹有區別不?有的,滿二叉樹必定是一個徹底二叉樹,反過來就不是了,感受不是很直觀,上圖一探究竟this

滿二叉樹

滿二叉樹

上圖就是一個滿二叉樹,也就是說除了葉子節點沒有左右孩子,其他節點都有左右孩子,這樣的二叉樹就是滿二叉樹spa

徹底二叉樹

這個樹不必定是滿的,可能這個節點沒有右孩子,也就是元素一層一層的碼放,每一層碼放滿了後在碼放下一層,不滿的那一部始終是在這個節點的右側。設計

在現實世界中滿二叉樹的狀況仍是不多的,更多的狀況是像徹底二叉樹這樣,總有那麼幾個元素孤零零在最後一層,相對來講徹底二叉樹更適用一些,3d

咱們再來看一下這張圖,徹底二叉樹的葉子節點可能都在最下層,也可能在最下層的上一層,圖中就是這樣的狀況code

二叉堆的幾個特色

二叉堆有最大堆最小堆,文章一開始圖中就有這倆兄弟,他們惟一的區別在於,父親節點和孩子節點的關係。最大堆是孩子節點不大於其父親節點的值,最小堆是孩子節點不小於父親節點cdn

這裏的說的最大是根節點最大,最小是根節點最小,以此類推這個節點都大於他的孩子節點的值或小於。這就是特色一,要麼是最大堆,要麼是最小堆

在說第二個特色前咱們思考一個問題,節點元素的大小與節點所處的層級有關係嗎? 從圖中看有練習,在上層也就是靠近根節點的元素大於底層元素,結果是這樣嗎,仔細發現不是這樣,從圖中能夠看到1619層級要高,可是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;
    }
}
複製代碼
相關文章
相關標籤/搜索