一. 认识队列
我们也先来认识一下队列, 看看它的特点和应用场景等.
队列结构
-
队列(Queue),它是一种运算受限的线性表,先进先出(FIFO First In First Out)
- 队列是一种受限的线性结构
- 受限之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作
-
生活中类似的队列结构
- 生活中类似队列的场景就是非常多了, 比如在电影院, 商场, 甚至是厕所排队.
- 优先排队的人, 优先处理. (买票, 结账, WC)
队列的图解
-
队列在程序中的应用
-
打印队列:
- 有五份文档需要打印, 这些文档会按照次序放入到打印队列中.
- 打印机会依次从队列中取出文档, 优先放入的文档, 优先被取出, 并且对该文档进行打印.
- 以此类推, 直到队列中不再有新的文档.
-
线程队列:
- 在进行多线程开发时, 我们不可能无限制的开启新的线程.
- 这个时候, 如果有需要开启线程处理任务的情况, 我们就会使用线程队列.
- 线程队列会依照次序来启动线程, 并且处理对应的任务.
-
二. 队列实现
我们来实现一个类, 用于模拟队列中的操作
队列的创建
- 我们需要创建自己的类, 来表示一个队列
// 自定义队列
function Queue() {
var items = []
// 队列操作的方法
}
-
代码解析:
- 我们创建了一个Queue构造函数, 用户创建队列的类.
- 在构造函数中, 定义了一个变量, 这个变量可以用于保存当前队列对象中所有的元素. (和创建栈非常相似)
- 这个变量是一个数组类型. 我们之后在队列中添加元素或者删除元素, 都是在这个数组中完成的.
- 队列和栈一样, 有一些相关的操作方法, 通常无论是什么语言, 操作都是比较类似的.
队列的操作
-
队列有哪些常见的操作呢?
enqueue(element):向队列尾部添加一个(或多个)新的项。dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素。front():返回队列中第一个元素——最先被添加,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息——与Stack类的peek方法非常类似)。isEmpty():如果队列中不包含任何元素,返回true,否则返回false。size():返回队列包含的元素个数,与数组的length属性类似。
-
现在, 我们来实现这些方法. (其实已经比较简单了)
-
enqueue方法
// enter queue方法
this.enqueue = function (element) {
items.push(element)
}
dequeue方法
- 注意: 从队列中删除元素不可以删除最后一个元素了.
- 因为, 先进入队列中的元素, 先从队列中取出. 因此, 应该删除第一个元素
// delete queue方法
this.dequeue = function () {
return items.shift()
}
front()方法
// 查看前端的元素
this.front = function () {
return items[0]
}
isEmpty方法
// 查看队列是否为空
this.isEmpty = function () {
return items.length == 0
}
size方法
// 查看队列中元素的个数
this.size = function () {
return items.length
}
完整的代码
- 我们来看一下队列完整的代码
// 自定义队列
function Queue() {
var items = []
// 队列操作的方法
// enter queue方法
this.enqueue = function (element) {
items.push(element)
}
// delete queue方法
this.dequeue = function () {
return items.shift()
}
// 查看前端的元素
this.front = function () {
return items[0]
}
// 查看队列是否为空
this.isEmpty = function () {
return items.length == 0
}
// 查看队列中元素的个数
this.size = function () {
return items.length
}
}
队列的使用
- 我们来简单使用一下我们封装的Queue
// 创建队列对象
var queue = new Queue()
// 在队列中添加元素
queue.enqueue("abc")
queue.enqueue("cba")
queue.enqueue("nba")
// 查看一下队列前端元素
alert(queue.front())
// 查看队列是否为空和元素个数
alert(queue.isEmpty())
alert(queue.size())
// 从队列中删除元素
alert(queue.dequeue())
alert(queue.dequeue())
alert(queue.dequeue())
队列面试题
击鼓传花是一个常见的面试算法题. 使用队列可以非常方便的实现最终的结果
击鼓传花的规则
-
原游戏规则:
- 班级中玩一个游戏, 所有学生围成一圈, 从某位同学手里开始向旁边的同学传一束花.
- 这个时候某个人(比如班长), 在击鼓, 鼓声停下的一颗, 花落在谁手里, 谁就出来表演节目.
-
修改游戏规则:
- 我们来修改一下这个游戏规则.
- 几个朋友一起玩一个游戏, 围成一圈, 开始数数, 数到某个数字的人自动淘汰.
- 最后剩下的这个人会获得胜利, 请问最后剩下的是原来在哪一个位置上的人?
击鼓传花的实现
- 我们使用队列可以非常方便的实现这个代码.
- 封装函数
// 实现击鼓传花的函数
function passGame(nameList, num) {
// 1.创建一个队列, 并且将所有的人放在队列中
// 1.1.创建队列
var queue = new Queue()
// 1.2.通过for循环, 将nameList中的人放在队列中
for (var i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i])
}
// 2.寻找最后剩下的人
while (queue.size() > 1) {
// 将前num-1中的人, 都从队列的前端取出放在队列的后端
for (var i = 0; i < num; i++) {
queue.enqueue(queue.dequeue())
}
// 将第num个人, 从队列中移除
queue.dequeue()
}
// 3.获取剩下的一个人
alert(queue.size())
var endName = queue.dequeue()
alert("最终留下来的人:" + endName)
// 4.获取该人在队列中的位置
return nameList.indexOf(endName)
}
代码验证:
// 验证结果
var names = ['John','Jack','Camila','Ingrid','Carl'];
var index = passGame(names, 7) // 数到8的人淘汰
alert("最终位置:" + index)