php實現一個簡單的堆

吳軍旗發表於2018-01-11

主要介紹優先佇列,通過優先佇列引出堆, 然後寫了一個類(php程式碼)實現了大根堆

原文請訪問我的部落格番茄技術小棧

優先佇列

定義

優先佇列是電腦科學中的一類抽象資料型別。優先佇列中的每個元素都有各自的優先順序,優先順序最高的元素最先得到服務;優先順序相同的元素按照其在優先佇列中的順序得到服務。優先佇列往往用堆來實現。

特點

  • 普通佇列:先進先出,後進後出
  • 優先佇列:出隊順序和入隊順序無關,和優先順序相關

現實用途

  • 動態任務處理中心(作業系統)

為什麼使用優先佇列

問題

  • 在1000000個元素中選出前100名
  • 在N個元素中選出前M個元素

解決方法

  • 排序,時間複雜度O(NlogN)
  • 使用優先佇列:時間複雜度(NlogM)

優先佇列的實現對比

paste image

定義

堆的實現通過構造二叉堆(binary heap),實為二叉樹的一種;由於其應用的普遍性,當不加限定時,均指該資料結構的這種實現。這種資料結構具有以下性質。

  • 任意節點小於(或大於)它的所有後裔,最小元(或最大元)在堆的根上(堆序性)。
  • 堆總是一棵完全樹。即除了最底層,其他層的節點都被元素填滿,且最底層儘可能地從左到右填入。
  • 將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。

paste image

用陣列儲存二叉堆

paste image

程式碼實現

<?php
require('../Library/SortTestHelper.php');
/**
 * 最大堆
 */
class MaxHeap{


	private $data;
	private $count;

	public function __construct(){
		$this->data = array();
		$this->count = 0;
	}


	// public function __construct($arr){
	// }

	public function insert($item){

		//從1開始
        $this->data[$this->count + 1] = $item;
        $this->_shiftUp($this->count+1);
        $this->count++;
    }

    public function  extractMax(){
        $ret = $this->data[1];
        swap( $this->data, 1 , $this->count);
        $this->count--;
        $this->_shiftDown(1);
        return $ret;
    }

    public function getMax(){
        return $this->data[1];
    }

    public function isEmpty(){
        return $this->count == 0;
    }

    public function getData(){
    	return $this->data;
    }

    /**
     * [_shiftUp 新加入到堆中的元素直接放在陣列後面,再與父元素比較後交換位置,直到根節點]
     * @param  [type] $k [description]
     * @return [type]    [description]
     */
	private function _shiftUp($k){
		//如果葉子節點的值比父元素大交換位置,並更新k的值
        while( $k > 1 && $this->data[(int)($k/2)] < $this->data[$k] ){
            // swap( $this->data[(int)($k/2)], $this->data[$k] );
            swap( $this->data, (int)($k/2) , $k);
            $k = (int)($k/2);
        }
    }

    /**
     * [_shiftDown 元素出堆的時候,需要維護此時的堆依然是一個大根堆, 此時將陣列元素的最後一個值與第一個值交換,後從上往下維護堆的性質]
     * @param  [type] $k [description]
     * @return [type]    [description]
     */
    private function _shiftDown($k){
    	//2k代表該節點的左子節點
        while( 2*$k <= $this->count ){
            $j = 2*$k;
            //判斷右節點是否存在,並且右節點大於左節點
            if( $j+1 <= $this->count && $this->data[$j+1] > $this->data[$j] ) $j ++;
            if( $this->data[$k] >= $this->data[$j] ) break;
            // swap( $this->data[$k] , $this->data[$j] );
            swap( $this->data, $k , $j );
            $k = $j;
        }
    }
}

$head_obj = new MaxHeap();
$n = 10;
for ($i=0; $i < $n; $i++) {
	$head_obj -> insert(rand(0, 1000));
}

print_r($head_obj -> getData());

while (!$head_obj -> isEmpty()) {
	echo $head_obj -> extractMax()."\n";
}
?>

複製程式碼

結果

生成的堆為:
Array
(
    [1] => 916
    [2] => 776
    [3] => 590
    [4] => 615
    [5] => 764
    [6] => 539
    [7] => 95
    [8] => 167
    [9] => 23
    [10] => 374
)
列印大根堆為:
916
776
764
615
590
539
374
167
95
23
複製程式碼

-------------------------華麗的分割線--------------------

看完的朋友可以點個喜歡/關注,您的支援是對我最大的鼓勵。

個人部落格番茄技術小棧掘金主頁

想了解更多,歡迎關注我的微信公眾號:番茄技術小棧

番茄技術小棧

相關文章