隊列和棧有着明顯的區別,隊列是一種特殊的線性表有着先進先出的特色。它只容許在表頭進行刪除操做,在表尾進行添加操做。javascript
入隊列示意圖 java
出隊列示意圖 面試
隊列有許多的應用,好比javascript
的事件循環機制,就是經過事件隊列
來存儲異步操做的回調函數。數組
好比逐層打印一顆樹上的節點。像kafka,rabbitmq這類消息隊列,其形式就是一種隊列,消息生產者把消息放入隊列中(尾部),消費者從隊列裏取出消息進行處理(頭部),只不過背後的實現更爲複雜。數據結構
若是你瞭解一點socket,那麼你應該知道當大量客戶端向服務端發起鏈接,而服務端忙不過來的時候,就會把這些請求放入到隊列中,先來的先處理,後來的後處理,隊列滿時,新來的請求直接拋棄掉。異步
數據結構在系統設計中的應用很是普遍,只是咱們水平達不到那個級別,知道的太少,但若是能理解並掌握這些數據結構,那麼就有機會在工做中使用它們並解決一些具體的問題,當咱們手裏除了錘子還有電鋸時,那麼咱們的眼裏就不僅是釘子,解決問題的思路也會更加開闊socket
首先先定義一些經常使用的方法:函數
而後咱們逐一實現學習
let Queue = (function () {
let items = new WeakMap()
// WeakMap結構與Map結構基本相似。區別是它只接受對象做爲鍵名,
// 不接受其餘類型的值做爲鍵名。鍵名是對象的弱引用,當對象被回收後,
// WeakMap自動移除對應的鍵值對,WeakMap結構有助於防止內存泄漏。
class Queue {
constructor() {
items.set(this, [])
}
// 入隊列
enqueue(item) {
let queue = items.get(this)
queue.push(item)
}
// 出隊列
dequeue() {
return items.get(this).shift()
}
// 返回隊列頭
head() {
let queue = items.get(this)
return queue[0]
}
// 返回隊列大小
size() {
let queue = items.get(this)
return queue.length
}
// 清空隊列
clear() {
items.set(this, [])
}
// 判斷隊列是否爲空
isEmpty() {
let queue = items.get(this)
return queue.length === 0
}
// 返回隊尾
tail() {
let queue = items.get(this)
return queue[queue.length - 1]
}
}
return Queue
})
複製代碼
有一個數組a[100]存放0--99;要求每隔兩個數刪掉一個數,到末尾時循環至開頭繼續進行,求最後一個被刪掉的數。好比:前10個數是 0 1 2 3 4 5 6 7 8 9 10,所謂每隔兩個數刪掉一個數,其實就是把 2 5 8 刪除掉。ui
// 先初始化一個數據
var arr_list = [];
for(var i=0;i< 100;i++){
arr_list.push(i);
}
// 定義功能函數
function del(arr) {
let queue = new Queue()
// 將全部元素入隊列
for(var i=0;i< arr_list.length;i++){
queue.enqueue(arr_list[i]);
}
let index = 0
while(queue.size() != 1) {
index ++
index%3 === 0 ? queue.enqueue(queue.dequeue()) : queue.dequeue()
}
return queue.head() // 返回隊列中惟一的元素
}
// 調用
del(arr_list)
複製代碼
push, 實現push方法時,若是兩個隊列都爲空,那麼默認向queue_1裏添加數據,若是有一個不爲空,則向這個不爲空的隊列裏添加數據
top,兩個隊列,或者都爲空,或者有一個不爲空,只須要返回不爲空的隊列的尾部元素便可
pop,pop方法是比較複雜,pop方法要刪除的是棧頂,但這個棧頂元素實際上是隊列的尾部元素。每一次作pop操做時,將不爲空的隊列裏的元素一次刪除並放入到另外一個隊列中直到遇到隊列中只剩下一個元素,刪除這個元素,其他的元素都跑到以前爲空的隊列中了。
function queueToStack() {
let queue_1 = new Queue()
let queue_2 = new Queue()
let data_queue = null
let empty_queue = null
// 確認每一個隊列的用途
let initQueue = () => {
if (queue_1.isEmpty() && queue_2.isEmpty()) {
data_queue = queue_1
empty_queue = queue_2
} else if (queue_1.isEmpty()) {
data_queue = queue_2
empty_queue = queue_1
} else {
data_queue = queue_1
empty_queue = queue_2
}
}
this.push = (item) => {
initQueue()
data_queue.enqueue(item)
}
this.top = () => {
initQueue()
return data_queue.tail()
}
this.pop = () => {
initQueue()
while (data_queue.size() > 1) {
empty_queue.enqueue(data_queue.dequeue())
}
return data_queue.dequeue()
}
}
複製代碼
隊列還有其餘不少在面試中可能會問道的面試題好比:打印楊輝三角以及迷宮問題,這些用隊列來實現可能會更加的方便。
function PriorityQueue() {
let items = []
function QueueElement(element, priority) {
this.element = element
this.priority = priority // 設置優先級別
}
this.enqueue = function(element, priority) {
let queueElement = new QueueElement(element, priority)
let add = false
for(let i = 0; i < items.length; i++) { // 遍歷隊列找到優先級比它小的元素
if(queueElement.priority > items[i].priority) {
items.splice(i, 0, queueElement) // 而後添加到該隊列
add = true
break
}
}
if(!add) { // 若是沒有找到直接添加到隊列末尾
items.push(queueElement)
}
}
}
複製代碼
function hotPotato(nameList, num) {
let queue = new Queue()
for(let i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i])
}
let eliminated = ''
while(queue.size() > 1) {
for(let i = 0; i < num; i++) {
queue.enqueue(queue.dequeue)
}
eliminated = queue.dequeue()
console.log(eliminates + 'out')
}
return queue.dequeue()
}
複製代碼
在學習了隊列以後與棧對比進一步瞭解了各個數據結構的用法,以及使用的方便之處。固然也爲面試打下了基礎。