PHP 優先級隊列:SplPriorityQueue

PHPSPL 庫內置了 SplPriorityQueue優先級隊列,而且是以Heap數據結構實現的,默認爲MaxHeap模式,即priority越大越優先出隊,同時能夠經過重寫compare方法來使用MinHeap(優先級越低越優先出隊,場景貌似不多吧)。php

SplPriorityQueue

堆特性

這裏須要注意並理解:SplPriorityQueue是以數據結構來實現的,當咱們出隊時會拿出堆頂的元素,此時的特性被破壞,會進行相應的調整至穩定態(MaxHeap or MinHeap),即會將最後一個元素替換到堆頂,而後進行穩定態驗證,不符合堆特性則繼續調整,或者咱們就獲得了一個穩定態,因此當優先級相同,出隊順序並不會按照入隊順序。數據結構

源碼示例:函數

<?php
$splPriorityQueue = new \SplPriorityQueue();
// 設定返回數據的meta信息
// \SplPriorityQueue::EXTR_DATA 默認 只返回數
// \SplPriorityQueue::EXTR_PRIORITY 只返回優先級
// \SplPriorityQueue::EXTR_BOTH 返回數據和優先級
// $splPriorityQueue->setExtractFlags(\SplPriorityQueue::EXTR_DATA);
$splPriorityQueue->insert("task1", 1);
$splPriorityQueue->insert("task2", 1);
$splPriorityQueue->insert("task3", 1);
$splPriorityQueue->insert("task4", 1);
$splPriorityQueue->insert("task5", 1);

echo $splPriorityQueue->extract() . PHP_EOL;
echo $splPriorityQueue->extract() . PHP_EOL;
echo $splPriorityQueue->extract() . PHP_EOL;
echo $splPriorityQueue->extract() . PHP_EOL;
echo $splPriorityQueue->extract() . PHP_EOL;

//執行結果
task1
task5
task4
task3
task2

能夠看到,雖然 5 個任務的優先級相同,但隊列並無按照入隊順序返回數據,由於的特性使然:
一、入隊 task1, task2, task3, task4, task5,由於優先級相同,因此堆一直處於穩定態。
二、出隊,得 task1,堆先將結構調整爲 task5, task2, task3, task4,已然達到了穩定態。
三、出隊,得 task5,堆先將結構調整爲 task4, task2, task3,已然達到了穩定態。
四、出隊,得 task4,堆先將結構調整爲 task3, task2,已然達到了穩定態。
五、出隊,得 task3,堆先將結構調整爲 task2,已然達到了穩定態。
四、出隊,得 task2。指針

Iterator, Countable

SplPriorityQueue實現了 Iterator, Countable接口,因此咱們能夠foreach/count函數操做它,或者使用rewind,valid,current,next/count方法。code

注意,由於是實現,因此rewind方法是一個no-op沒有什做用的操做,由於頭指針始終指向堆頂,即current始終等於top,不像List只是遊走指針,出隊是會刪除堆元素的,extract = current + next(current出隊,從堆中刪除)。接口

<?php
$splPriorityQueue = new \SplPriorityQueue();

$splPriorityQueue->insert("task1", 1);
$splPriorityQueue->insert("task2", 2);
$splPriorityQueue->insert("task3", 1);
$splPriorityQueue->insert("task4", 4);
$splPriorityQueue->insert("task5", 5);

echo "Countable: " . count($splPriorityQueue) . PHP_EOL;

// 迭代的話會刪除隊列元素 current 指針始終指向 top 因此 rewind 沒什麼意義
for ($splPriorityQueue->rewind(); $splPriorityQueue->valid();$splPriorityQueue->next()) { 
    var_dump($splPriorityQueue->current());
    var_dump($splPriorityQueue->count());
    $splPriorityQueue->rewind();
}

var_dump("is empty:" . $splPriorityQueue->isEmpty());

Extract出隊

extract 出隊更爲友好,即始終返回優先級最高的元素,優先級相投時會以堆調整的特性返回數據。隊列

<?php
$splPriorityQueue = new \SplPriorityQueue();

// data  priority
$splPriorityQueue->insert("task1", 1);
$splPriorityQueue->insert("task2", 2);
$splPriorityQueue->insert("task3", 1);
$splPriorityQueue->insert("task4", 4);
$splPriorityQueue->insert("task5", 5);

echo "Countable: " . count($splPriorityQueue) . PHP_EOL;

while (! $splPriorityQueue->isEmpty()) {
    var_dump($splPriorityQueue->extract());
    echo $splPriorityQueue->count() . PHP_EOL;
}

自定義優先級處理方式

重寫compare方法定義本身的優先級處理機制。源碼

<?php
class CustomedSplPriorityQueue extends SplPriorityQueue
{
    public function compare($priority1, $priority2): int
    {
        // return $priority1 - $priority2;//高優先級優先
        return $priority2 - $priority1;//低優先級優先
    }
}

$splPriorityQueue = new \CustomedSplPriorityQueue();
$splPriorityQueue->setExtractFlags(SplPriorityQueue::EXTR_BOTH);
$splPriorityQueue->insert("task1", 1);
$splPriorityQueue->insert("task2", 2);
$splPriorityQueue->insert("task3", 1);
$splPriorityQueue->insert("task4", 4);
$splPriorityQueue->insert("task5", 5);
 
echo "Countable: " . count($splPriorityQueue) . PHP_EOL;

while (!$splPriorityQueue->isEmpty()) {
    var_dump($splPriorityQueue->extract());
}
相關文章
相關標籤/搜索