堆 構造 堆排序

2022-09-12 01:12:15 字數 2946 閱讀 6582

堆和堆的構建

什麼是堆

堆是一種特殊的二叉樹,具備以下特性:

如果每個節點的值都大於等於左右孩子節點的值,這樣的堆叫大頂堆;如果每個節點的值都小於等於左右孩子節點的值,這樣的堆叫小頂堆。

上圖中,左側的堆是大頂堆,右側的堆是小頂堆,我們還可以得出這個結論:對應大頂堆,堆頂一定是最大值;對於小頂堆,堆頂一定是最小值。

如何構建堆

我們在介紹二叉樹的定義和儲存的時候說到,由於完全二叉樹的特殊性,可以通過陣列來儲存,堆也是完全二叉樹,所以我們完全可以通過陣列來儲存。在使用陣列儲存堆的時候,把第乙個索引位置留空,從第二個索引位置開始儲存堆元素,這樣,對於索引值為i的元素而言,其子節點索引分別為2i2i+1

下面我們就來看如何在堆中插入新節點,以大頂堆為例,從葉子結點插入,如果比父級元素大,則與父級元素交換位置,依次類推,直到到達根節點(小頂堆恰好相反):

注:構建堆的過程叫堆化

實現**下面是對應的 php 實現**:

<?php 

class heap

public function insert($data)

$this->count++;

$this->a[$this->count] = $data;

$i = $this->count;

while (floor($i/2) > 0 && $this->a[floor($i/2)] < $this->a[$i])

return true;

}public function __tostring()

}

我們可以為這段**編寫一段測試**:

$heap = new heap();

$data = range(1, 10);

shuffle($data);

foreach ($data as $num)

}print_r($heap);

列印結果如下,符合堆定義,表明堆構建成功:

堆排序及其應用

堆排序

但是這裡有乙個問題,每次從堆頂刪除元素後,需要從子節點中取值補齊堆頂,依次類推,直到葉子節點,就會致使儲存堆的陣列出現「空洞」:

解決辦法是將陣列中的最後乙個元素(最右邊的葉子節點)移到堆頂,再重新對其進行堆化:

這樣,就完美解決了「陣列空洞」的問題。

實現**

下面我們將堆排序過程轉化為對應的 php 實現**如下:

<?php 

class heap

public function insert($data)

$this->count++;

$this->a[$this->count] = $data;

$i = $this->count;

while (floor($i/2) > 0 && $this->a[floor($i/2)] < $this->a[$i])

return true;

}public function remove()

if ($i*2 + 1 <= $this->count && $this->a[$i*2+1] > $this->a[$maxpos])

if ($maxpos == $i)

$temp = $this->a[$i];

$this->a[$i] = $this->a[$maxpos];

$this->a[$maxpos] = $temp;

$i = $maxpos;

}return $removedata;

}public function __tostring()

}

我們可以結合堆化和刪除方法,寫一段測試**:

$heap = new heap();

$data = range(1, 10);

shuffle($data);

foreach ($data as $num)

}$sorteddata = ;

while ($removeddata = $heap->remove())

print_r($sorteddata);

列印的結果如下:

說明堆排序成功,資料變成了從大到小的排序序列。

複雜度分析

我們先看時間複雜度,對堆排序而言,分為兩個階段,乙個是堆的構建,乙個是堆頂元素的刪除。對於n個節點的堆化而言,通過陣列儲存,對應的時間複雜度是o(n),對於堆頂元素的刪除而言,需要遍歷n個節點,並且,每次刪除後需要重新堆化,對應的平均時間複雜度是o(nlogn)。所以綜合下來,堆排序的時間複雜度和快速排序、歸併排序一樣,是o(nlogn)

堆排序的過程中,涉及到不相鄰元素的交換(刪除堆頂元素的時候),所以不是穩定的排序演算法。

我們在刪除堆頂元素的時候,使用了額外的儲存空間存放被刪除的堆頂元素,但是,我們也可以對這個過程進行優化,從而做到原地排序,感興趣的同學可以試試。

堆排序的應用

排序 4 堆排序

將待排序的序列構成乙個大頂堆。此時,序列的最大值就是堆頂的根結點。將它移走 就是,將其與堆陣列的末尾元素交換,此時末尾的元素就是最大值 然後將剩餘的n 1個序列重新構成乙個堆,如此反覆。其中關鍵問題 1 如何將無序序列構成乙個堆 2 輸出堆頂元素後,如何調整公升序元素為乙個堆 4.堆排序 堆調整 v...

排序二 堆排序

子結點的鍵值或索引總是小於 或者大於 它的父節點。堆通過一維陣列實現。在起始陣列為 0 的情形中 父節點i的左子節點在位置 2 i 1 父節點i的右子節點在位置 2 i 2 子節點i的父節點在位置 floor i 1 2 堆中的最大值總是位於根節點。堆中定義以下幾種操作 1 最大堆調整 max he...

排序七 堆排序

時間複雜度 最好 o n o n 平均o n o nlogn 最差o n o nlogn 空間複雜度 o n o 1 穩定性 不穩定 堆 順序儲存的完全二叉樹,其每個結點均小於等於或大於等於其子節點。小根堆 每個結點元素的值都小於等於其子節點元素的值的堆。s i s 2 i 1 s i s 2 i ...