队列:
队列结构:Queue,也是一种常见的数据结构。它是一种受限的线性结构,在队尾插入元素,在队首删除元素,先进先出(FIFO,first in first out)。
生活中的队列:
比如:在银行排队办理业务,后到的人只能排在队伍的末端,开始营业时队伍前端的人先办理业务。
程序中的队列:
比如:JS 是单线程的,在执行代码时会有多个任务,这些任务会在线程中排队按顺序依次执行。
队列的实现:
本章基于数组来实现队列结构。
// 封装队列
function Queue() {
// 队列中的元素
this.items = []
}
// 队列中的操作
// 向队列尾部添加一个新的项
Queue.prototype.enqueue = function(element) {
this.items.push(element)
}
// 移除队首的第一个项,并返回被移除的元素
Queue.prototype.dequeue = function() {
this.items.shift()
}
// 返回队首的第一个元素,队列不做任何变动
Queue.prototype.front = function() {
return this.items[0]
}
// 如果队列中没有任何元素就返回 true,否则返回 false
Queue.prototype.isEmpty = function() {
return this.items.length === 0
}
// 返回队列中的元素个数
Queue.prototype.size = function() {
return this.items.length
}
// 将队列结构的内容以字符串形式返回
Queue.prototype.toString = function() {
return this.items.join(' ')
}
// 调用队列
var q = new Queue()
q.enqueue(10)
q.enqueue(20)
console.log(q.toString())
队列的应用:
将击鼓传花的游戏规则改一下:几个朋友一起玩一个游戏,围成一圈开始数数,数到某个数字的人自动淘汰,由他后面的人再次重新开始数数,一直到最后剩下的人获得胜利,问最后剩下的人在原来的哪一个位置?
// 击鼓传花游戏
function passGame(nameList, num) {
// 创建一个队列结构
var queue = new Queue()
// 将所有人依次加入到队列中
for(var i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i])
}
// 开始数数字:只要队列中剩余的人数大于1就一直循环数数
while(queue.size() > 1) {
// num 数字之前的人,从原队列中删除并重新加入到队列的末尾
for(var i = 0; i < num - 1; i++) {
var name = queue.dequeue()
queue.enqueue(name)
}
// 是 num 这个数字的人,将其从队列中删除
queue.dequeue()
}
// 返回最终剩下的那个人
return queue.front()
}
console.log(passGame(['Tom', 'Mary', 'Lee', 'Lucy', 'Lily'], 3)) // Lucy
优先级队列:
在普通的队列中插入一个元素,元素会被放在队尾,并且需要前面所有的元素都处理完后才会处理该元素。
而优先级队列,在插入一个元素的时候就会考虑该元素的优先级,会将该元素与其他元素的优先级进行比较,从而得出这个元素在队列中的正确的位置。
生活中的优先级队:
比如:机场登机的顺序,头等舱和商务舱乘客登机的优先级要高于经济舱乘客。
程序中的优先级队列:
比如:每个线程处理的任务的重要性不同,就可以通过优先级的大小,来决定该线程在队列中被处理的顺序。
优先级队列的实现:
在实现优先级队列时,需要考虑两个问题:每个元素不再只是一个数据,而且还需要包含数据的优先级;在添加元素的时候,需要将元素的优先级和队列中已经存在的元素的优先级进行比较,将其放入正确的位置。
// 封装节点类
function Node(element, priority) {
// 优先级队列中的节点需要包含两个内容:一个是元素本身的数据,另一个是优先级
this.element = element
this.priority = priority
}
// 封装优先级队列:值越小,越排列在前面
function PriorityQueue() {
// 队列中的元素
this.items = []
}
// 队列中的操作
// 向队列中添加一个新的项
PriorityQueue.prototype.enqueue = function(element, priority) {
// 1. 创建节点
var node = new Node(element, priority)
// 2. 判断队列是否为空,为空的话将元素直接添加进去
if (this.isEmpty()) {
this.items.push(node)
return false
}
// 3. 不为空则进行比较
var addred = false
for(var i = 0; i < this.size(); i++) {
// 是按优先级从小到大排列的,因此判断到当前元素的优先级比某个元素小,插入到这个元素前面
if (priority < this.items[i].priority) {
this.items.splice(i, 0, node)
addred = true
break
}
}
// 4. 如果当前元素的优先级比队列中任何一个元素的都大,则插入到队尾
if (!addred) {
this.items.push(node)
}
}
// 移除队首的第一个项,并返回被移除的元素
PriorityQueue.prototype.dequeue = function() {
return this.items.shift()
}
// 返回队首的第一个元素,队列不做任何变动
PriorityQueue.prototype.front = function() {
return this.items[0]
}
// 如果队列中没有任何元素就返回 true,否则返回 false
PriorityQueue.prototype.isEmpty = function() {
return this.items.length === 0
}
// 返回队列中的元素个数
PriorityQueue.prototype.size = function() {
return this.items.length
}
// 将队列结构的内容以字符串形式返回
PriorityQueue.prototype.toString = function() {
var resultStr = ''
for(var i = 0; i < this.size(); i ++) {
resultStr += `${this.items[i].element}-${this.items[i].priority} `
}
return resultStr
}
// 调用优先级队列
var pq = new PriorityQueue()
pq.enqueue('Mary', 40)
pq.enqueue('Lily', 10)
pq.enqueue('Tom', 20)
pq.enqueue('Bob', 30)
console.log(pq.toString()) // Lily-10 Tom-20 Bob-30 Mary-40