队列是一种先进先出(First-In-First-Out,FIFO)的数据结构。可以联想一下小朋友排队打疫苗, 排在前头的先打, 排在后边的后打。打完疫苗的朋友就可以回家了(出队),刚到的朋友需要排队(入队)。
class Queue { constructor(store = []) { this.store = store } // 入队 enqueue(...args) { this.store.push(...args) } // 出队 dequeue() { return this.store.shift() } // 队首元素 head() { return this.store[0] } // 队尾元素 tail() { return this.store[this.size() - 1] } // 元素个数 size() { return this.store.length } // 元素是否为空 isEmpty() { return this.size() === 0 } // 清空 clear() { this.store = [] } }
优先队列元素的添加和移除是基于优先级的。元素入队时不是依次入队, 而是根据优先级进行排序入队, 优先级高的元素永远排在优先级低的元素之前,并且比优先级低的元素先出队。这个可以联想登机时分为头等舱、经济舱的场景, 头等舱的乘客享有优先登机的服务。
/** * 元素类, 每个元素包含优先级和元素值 */ class QueueElement { constructor(element, priority) { this.element = element this.priority = priority } } /** * 优先队列, 只需要重写父类的入队函数即可 * 别的方法可以直接继承自 Queue */ class PriorQueue extends Queue { constructor(store) { super(store) } /** * 入队 * 1. 若队列为空, 则直接入队 * 2. 否则, 查找队列中优先级比入队元素优先级低的元素 * 2.1 若存在, 则入队位置为该元素的索引, 该索引后边的元素依次向后移动一个位置 * 2.2 若不存在, 则入队元素的优先级最低, 入队位置为队列末尾 */ enqueue(element, priority) { const insertedElement = new QueueElement(element, priority) if (this.isEmpty()) { this.store.push(insertedElement) } else { let isEnqueue = false for (let i = 0; i < this.size(); i++) { const currentElement = this.store[i] if (insertedElement.priority < currentElement.priority) { this.store.splice(i, 0, insertedElement) isEnqueue = true return } } // 若未找到优先级比入队元素大的元素, 说明入队元素的优先级最低 if (!isEnqueue) { this.store.push(insertedElement) } } } } // 简单测试 const qp = new PriorQueue([ new QueueElement('zeus', 1), new QueueElement('zeus', 2), ]) qp.enqueue('luna', 4) qp.enqueue('coco', 5) qp.enqueue('snk', 20) qp.enqueue('loa', 4) qp.enqueue('sv', 9) qp.enqueue('am', 4) qp.enqueue('es', 8) qp.enqueue('sk', 9) qp.enqueue('qop', 8) console.log(qp); console.log(qp.dequeue()) console.log(qp);
有限队列入队时, 有几种情况:
// 击鼓传花 const runPassFlower = nameList => { // 击鼓函数, 生成一个小于 10 的随机整数 N // 表示传递 N 次后, 击鼓一次 const hitDrum = ()=> { return Math.floor(Math.random() * 10) } const q = new Queue(nameList) while (q.size() > 1) { const drumPlot = hitDrum() for(let i = 0; i< drumPlot; i++) { q.enqueue(q.dequeue()) } const loser = q.dequeue() console.log(`${loser} \出局了`) } return q.head() } const nameList = ['张三','李四','王五','赵六','周七','吴八','郑九','钱十','萧十一','郭十二','管十三',] console.log(`~~~~ ${runPassFlower(nameList)} 胜出! ~~~~`)