隊列是另一種遵循先進先出原則的線性數據結構。隊列有兩端可供操做,一端出隊,一端入隊。這個特色和棧不一樣,棧只有一端能夠用來操做。入隊老是在後端,出隊在前端。php
首先咱們定義一個QueueInterface。前端
interface QueueInterface {
public function enqueue(string $item);
public function dequeue();
public function isEmpty();
public function peek();
}
複製代碼
來看基於數組的隊列實現git
class ArrQueue implements QueueInterface {
private $queue;
private $limit;
public function __construct(int $limit = 0) {
$this->limit = $limit;
$this->queue = [];
}
public function isEmpty() {
return empty($this->queue);
}
public function dequeue() {
if ($this->isEmpty()) {
throw new \UnderflowException('queue is empty');
} else {
array_shift($this->queue);
}
}
public function enqueue(string $item) {
if (count($this->queue) >= $this->limit) {
throw new \OverflowException('queue is full');
} else {
array_unshift($this->queue, $item);
}
}
public function peek() {
return current($this->queue);
}
}
複製代碼
得益於PHP強大的array結構,咱們垂手可得的寫出來了隊列的基本操做方法。果真世界上最好的語言名不虛傳。github
可能機智的同窗已經猜到了,我以前已經定義了一個隊列接口,那隊列的實現確定不止只有上面一種哈。來看基於鏈表的實現。web
class LinkedListQueue implements QueueInterface {
private $limit;
private $queue;
public function __construct(int $limit = 0) {
$this->limit = $limit;
$this->queue = new LinkedList();
}
public function isEmpty() {
return $this->queue->getSize() == 0;
}
public function peek() {
return $this->queue->getNthNode(0)->data;
}
public function enqueue(string $item) {
if ($this->queue->getSize() < $this->limit) {
$this->queue->insert($item);
} else {
throw new \OverflowException('queue is full');
}
}
public function dequeue() {
if ($this->isEmpty()) {
throw new \UnderflowException('queue is empty');
} else {
$lastItem = $this->peek();
$this->queue->deleteFirst();
return $lastItem;
}
}
}
複製代碼
裏面涉及到了以前的鏈表實現,不瞭解細節的同窗能夠去這裏看看。算法
強大的PHP已經內置了隊列實現,能夠使用的方法和上面咱們本身實現的相似。後端
class SqlQueue {
private $splQueue;
public function __construct() {
$this->splQueue = new \SplQueue();
}
public function enqueue(string $data = null) {
$this->splQueue->enqueue($data);
}
public function dequeue() {
return $this->splQueue->dequeue();
}
}
複製代碼
優先隊列是一種特殊的隊列,入隊或者出隊的順序都是基於每一個節點的權重。數組
優先隊列能夠分爲有序優先隊列和無序優先隊列。有序優先隊列又有兩種狀況,倒序或者順序。在順序序列中,咱們能夠迅速的找出最大或者最小優先級別的節點,複雜度是O(1)。可是插入的話會花費掉更多的時間,由於咱們要檢查每個節點的優先級別而後插入到合適的位置。數據結構
在無序序列中,在插入新節點的時候咱們不須要根據他們的優先級肯定位置。入隊的時候像普通隊列同樣,插入到隊列的末尾。可是當咱們想移除優先級最高的元素的時候,咱們要掃描每個節點來肯定移除優先級最高的那一個。接下來咱們仍是基於鏈表實現一個順序的優先隊列。數據結構和算法
class LinkedListPriorityQueue {
private $limit;
private $queue;
public function __construct(int $limit = 0) {
$this->limit = $limit;
$this->queue = new LinkedList();
}
public function enqueue(string $data = null, int $priority) {
if ($this->queue->getSize() > $this->limit) {
throw new \OverflowException('Queue is full');
} else {
$this->queue->insert($data, $priority);
}
}
public function dequeue(): string {
if ($this->isEmpty()) {
throw new \UnderflowException('Queue is empty');
} else {
$item = $this->peek();
$this->queue->deleteFirst();
return $item->data;
}
}
public function peek() {
return $this->queue->getNthNode(0);
}
public function isEmpty() {
return $this->queue->getSize() === 0;
}
}
複製代碼
爲充分利用向量空間,克服"假溢出"現象的方法是:將向量空間想象爲一個首尾相接的圓環,並稱這種向量爲循環向量。存儲在其中的隊列稱爲循環隊列。環形隊列也是一種數組,只是它在邏輯上把數組的頭和尾相連,造成循環隊列,當數組尾滿的時候,要判斷數組頭是否爲空,不爲空繼續存放數據。
class CircularQueue implements QueueInterface {
private $queue;
private $limit;
private $front = 0;
private $rear = 0;
public function __construct(int $limit = 0) {
$this->limit = $limit;
$this->queue = [];
}
public function isEmpty() {
return $this->front === $this->rear;
}
public function isFull() {
$diff = $this->rear - $this->front;
if ($diff == -1 || $diff == ($this->limit - 1)) {
return true;
}
return false;
}
public function peek() {
return $this->queue[$this->front];
}
public function dequeue() {
if ($this->isEmpty()) {
throw new \UnderflowException('Queue is empty');
}
$item = $this->queue[$this->front];
$this->queue[$this->front] = null;
$this->front = ($this->front + 1) % $this->limit;
return $item;
}
public function enqueue(string $item) {
if ($this->isFull()) {
throw new \OverflowException('Queue is full');
}
$this->queue[$this->rear] = $item;
$this->rear = ($this->rear + 1) % $this->limit;
}
}
複製代碼
截止目前咱們所實現的隊列都是在隊尾(rear)入隊,隊首(front) 出隊的結構。在特殊的狀況下,咱們但願不管是隊首仍是隊尾均可以入隊或者出隊,這種結構就叫作雙端隊列。基於咱們以前實現的鏈表結構,咱們能夠垂手可得的實現這樣的結構。
class LinkedListDeQueue {
private $limit = 0;
private $queue;
public function __construct(int $limit = 0) {
$this->limit = $limit;
$this->queue = new \DataStructure\LinkedList\LinkedList();
}
public function dequeueFromFront(): string {
if ($this->isEmpty()) {
throw new \UnderflowException('Queue is empty');
}
$item = $this->queue->getNthNode(0);
$this->queue->deleteFirst();
return $item->data;
}
public function dequeueFromBack(): string {
if ($this->isEmpty()) {
throw new \UnderflowException('Queue is empty');
}
$item = $this->queue->getNthNode($this->queue->getSize() - 1);
$this->queue->deleteLast();
return $item->data;
}
public function isFull() {
return $this->queue->getSize() >= $this->limit;
}
public function enqueueAtBack(string $data = null) {
if ($this->isFull()) {
throw new \OverflowException('queue is full');
}
$this->queue->insert($data);
}
public function enqueueAtFront(string $data = null) {
if ($this->isFull()) {
throw new \OverflowException('queue is full');
}
$this->queue->insertAtFirst($data);
}
public function isEmpty() {
return $this->queue->getSize() === 0;
}
public function peekFront() {
return $this->queue->getNthNode(0)->data;
}
public function peekBack() {
return $this->queue->getNthNode($this->queue->getSize() - 1)->data;
}
}
複製代碼
裏面涉及到了以前的鏈表實現,不瞭解細節的同窗能夠去這裏看看。
棧和隊列是咱們最經常使用的數據結構,咱們會在其餘的複雜數據結構中看到這兩種抽象數據類型的應用。在下一章,咱們繼續學習PHP數據結構之遞歸,這是一種將複雜問題簡單化的經常使用思路。
PHP基礎數據結構專題系列目錄地址:github.com/... 主要使用PHP語法總結基礎的數據結構和算法。還有咱們平常PHP開發中容易忽略的基礎知識和現代PHP開發中關於規範、部署、優化的一些實戰性建議,同時還有對Javascript語言特色的深刻研究。