PHP
的 SPL
库内置了 SplPriorityQueue
优先级队列,而且是以Heap
数据结构实现的,默认为MaxHeap
模式,即priority
越大越优先出队,同时能够经过重写compare
方法来使用MinHeap
(优先级越低越优先出队,场景貌似不多吧)。php
这里须要注意并理解: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。指针
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 出队更为友好,即始终返回优先级最高的元素,优先级相投时会以堆调整的特性返回数据。队列
<?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()); }