隊列你們應該都很熟悉,專業的說隊列是一種特殊的線性表,簡單的說就是先進先出(FIFO),與隊列相反的還有一種數據結構叫做棧,先進後出(FILO),這裏的棧和內存裏面的棧沒啥關係,不要理解錯了!php
隊列在開發的應用挺多的,最普遍的就是消息隊列,用來處理一些任務好比下單,搶購,須要按請求的時間排序,先來的先處理,關鍵是保持一種順序結構。實際開發中,咱們通常不多本身去實現隊列,一般都是使用一些現成的服務,好比redis queue,rabbitmq。redis
優先隊列(Priprity Queue),顧名思義,就是帶有優先級的隊列,也就是說不是按請求的順序排序,並且根據某一些規則屬性。舉個例子:有一些12306的刷票軟件,花錢買了加速包搶到票的概率更高。這裏所謂概率更高換個說法就是優先級更高,若是隻有10張票,確定是先讓那些花了錢的先搶到票,沒花錢的話排後面。算法
假設如今有10000我的搶票,其中有50我的交了數目不一的錢,當系統搶到一張票後須要按照這些用戶交錢的數目從大到小排序依次分配。若是讓你去實現上面所說的搶票優先級,你會怎麼設計呢?sql
若是這些用戶信息是存儲到數據庫裏面,當每次搶到一張票的時候,使用sql語句排序取出符合條件的用戶裏面交錢最多的那位就好了。若是不是存儲到數據庫裏面的,可能就須要在內存裏面排序了,1萬個用戶信息雖然很少,可是你每次都須要從新排序。shell
使用redis sorted set 實現,Redis 有序集合和集合同樣也是string類型元素的集合,且不容許重複的成員。不一樣的是每一個元素都會關聯一個double類型的分數,redis正是經過分數來爲集合中的成員進行從小到大的排序,有序集合的成員是惟一的,但分數(score)卻能夠重複。數據庫
sorted set 操做
ZADD:向 sorted set 中添加元素
ZCOUNT: sorted set 中 score 等於指定值的元素有多少個
ZSCORE:sorted set 中指定元素的 score 是多少
ZCARD: sorted set 中總共有多少個元素
ZREM:刪除 sorted set 中的指定元素
ZREVRANGE:按照從大到小的順序返回指定索引區間內的元素
ZRANGE: 按照從小到大的順序返回指定索引區間內的元素
複製代碼
值得一說的是,這個並非併發安全的,由於取優先級最高的元素以及刪除這個元素是兩次操做,不是原子性的,不過可使用lua腳本解決這個問題。編程
使用優先隊列,大部分編程語言的標準庫裏面都自帶優先隊列實現,並不須要本身去實現,不過像PHP這樣的Web程序每次請求結束後內存數據都會被銷燬,使用本身構建的優先隊列還不如第二種作法好使,或者實現一個常駐進程的服務供Web調用。數組
優先隊列是基於二叉堆的,構建一個優先隊列實際上就是在構建一個二叉堆,二叉堆是一種特殊的堆,二叉堆是徹底二元樹(二叉樹)或者是近似徹底二元樹(二叉樹)。安全
二叉堆有兩種:最大堆和最小堆。最大堆:父結點的鍵值老是大於或等於任何一個子節點的鍵值;最小堆:父結點的鍵值老是小於或等於任何一個子節點的鍵值。數據結構
二叉樹是每一個結點最多有兩個子樹的樹結構。
樹是一種非線性的數據結構,是由n(n >=0)個結點組成的有限集合。
以上內容僅供參考,關於這些數據結構的實現和算法細節這裏不說了,畢竟不簡單,感興趣的話能夠詳細瞭解一下。
這些算法雖然不簡單,可是畢竟咱們都是站在巨人的肩膀上,下面看一下在PHP SPL裏面提供的優先隊列實現。PHP的標準庫裏面提供了經常使用的數據結構,好比鏈表,堆,棧,最大堆,最小堆,固定大小數組,其中就有優先隊列,其類摘要以下:
SplPriorityQueue implements Iterator , Countable {
/* 方法 */
public __construct ( void )
public int compare ( mixed $priority1 , mixed $priority2 )
public int count ( void )
public mixed current ( void )
public mixed extract ( void )
public int getExtractFlags ( void )
public void insert ( mixed $value , mixed $priority )
public bool isCorrupted ( void )
public bool isEmpty ( void )
public mixed key ( void )
public void next ( void )
public void recoverFromCorruption ( void )
public void rewind ( void )
public void setExtractFlags ( int $flags )
public mixed top ( void )
public bool valid ( void )
}
複製代碼
其中經常使用的是compare,count,current,insert,next,rewind,valid等方法,用法也相對簡單,下面看一個完整的例子:
<?php
$queue = new SplPriorityQueue();
$queue->insert("A", 2);
$queue->insert("B", 17);
$queue->insert("C", 4);
$queue->insert("D", 10);
$queue->insert("E", 1);
//獲取優先級最高的元素
echo $queue->top()."\n";
//按照優先級從大到小遍歷全部元素
while ($queue->valid()) {
echo $queue->current()."\n";
$queue->next();
}
複製代碼
默認狀況下,這個是按照數值大小排序的,可是若是排序比較的屬性的並非一個數值怎麼辦呢?好比說是對象,這時候能夠採用下面的寫法,咱們能夠新建一個類繼承標準庫的類,而後根據本身的規則重寫compare的方法:
<?php
class MyQueue extends SplPriorityQueue {
public function compare($priority1, $priority2) {
if ($priority1->age === $priority2->age) {
return 0;
}
return $priority1->age < $priority2->age ? -1 : 1;
}
}
class Person {
public $age;
public function __construct($age) {
$this->age = $age;
}
}
$queue = new MyQueue();
$queue->insert("A", new Person(2));
$queue->insert("B", new Person(17));
$queue->insert("C", new Person(4));
$queue->insert("D", new Person(10));
$queue->insert("E", new Person(1));
//獲取優先級最高的元素
echo $queue->top() . "\n";
//按照優先級從大到小遍歷全部元素
while ($queue->valid()) {
echo $queue->current() . "\n";
$queue->next();
}
複製代碼
你們看懂了嗎?若是錯誤歡迎指正!