队列是遵循FIFO(First In First Out,先进先出,也称为先来先服务)原则的一组有序的项。
队列在尾部添加新元素,并从顶部移除元素。
最新添加的元素必须排在队列的末尾。
在现实中,最常见的队列的例子就是排队,排在第一位的人先接受服务。
一、创建队列
function Queue () {
var items = [];
/*
enqueue(element(s))
向队列尾部添加一个或多个新的项
*/
this.enqueue = function(element){
items.push(element);
};
/*
dequeue()
移除队列的第一(即排在队列最前面的)项,并返回被移除的元素
*/
this.dequeue = function(){
return items.shift();
};
/*
front()
返回队列中第一个元素(最先被添加,也将是最先被移除的元素)
队列不做任何变动,不移除元素,只返回元素信息
*/
this.front = function(){
return items[0];
};
/*
isEmpty()
如果队列中不包含任何元素,返回true,否则返回false
*/
this.isEmpty = function(){
return items.length == 0;
};
/*
clear()
移除队列中的所有元素
*/
this.clear = function(){
items = [];
};
/*
size()
返回队列的元素个数
和数组的length属性类似
*/
this.size = function(){
return items.length;
};
/*
print()
辅助方法,将队列中的元素输出到控制台
*/
this.print = function(){
console.log(items.toString());
};
}
二、使用Queue类
var queue = new Queue();
console.log(queue.isEmpty()); //输出true
//添加一些元素
queue.enqueue("John");
queue.enqueue("Jack");
queue.enqueue("Ben");
queue.print(); //输出John,Jack,Ben
console.log(queue.size()); //输出3
console.log(queue.isEmpty()); //输出false
queue.dequeue();
queue.print(); //输出Jack,Ben
queue.dequeue();
queue.print(); //输出Ben
输出结果如下:
三、优先队列
元素的添加和移除是基于优先级的。
举个例子,医院的(急诊科)候诊室,医生会优先处理病情比较严重的患者。
function PriorityQueue () {
var items = [];
/*
创建一个特殊的元素
这个元素包含了要添加到队列的元素(任意类型)及其在队列中的优先级
*/
function QueueElement(element, priority){
this.element = element;
this.priority = priority;
}
/*
添加元素
*/
this.enqueue = function(element, priority){
var queueElement = new QueueElement(element, priority);
//如果队列为空,直接将元素入列
if (this.isEmpty()) {
items.push(queueElement);
}
//否则比较该元素与其他元素的优先级
else {
var added = false;
for (var i = 0; i < items.length; i++) {
/*
当找到一个比要添加的元素的priority值更大(优先级更低)的项时,
就把新元素插入到它之前
(对于其他优先级相同,但是先添加到队列的元素,
同样遵循先进先出的原则)
*/
if (queueElement.priority < items[i].priority) {
items.splice(i, 0, queueElement);
added = true;
break;
}
}
/*
如果要添加元素的priority值大于任何已有的元素,
把它添加到队列的末尾就行了
*/
if (!added) {
items.push(queueElement);
}
}
};
/*其他方法与默认的Queue实现相同*/
this.dequeue = function(){
return items.shift();
};
this.front = function(){
return items[0];
};
this.isEmpty = function(){
return items.length == 0;
};
this.clear = function(){
items = [];
};
this.size = function(){
return items.length;
};
//与默认的稍有不同
this.print = function () {
var temp = [];
for(var i = 0; i < items.length; i++){
temp.push(items[i].element);
}
console.log(temp.toString());
};
}
【注】处理添加元素的方法与默认的不一样外,还有print()方法也略有不同。
在默认的队列中,定义的items数组只存储了element,但是在优先队列中的items里,除了element,还有priority(优先级),这时候为了在控制台上只输出显示队列中的元素,就需要一个临时数组temp来存储element,具体实现看上面的代码。
测试代码如下:
var priorityQueue = new PriorityQueue();
priorityQueue.enqueue("John", 2);
priorityQueue.print();
priorityQueue.enqueue("Jack", 1);
priorityQueue.print();
priorityQueue.enqueue("Ben", 1);
priorityQueue.print();
第一个添加的元素是优先级为2的John,因为此前队列为空,所以它是队列中唯一的元素。
然后,添加优先级为1的Jack,由于Jack的优先级高于John,Jack就成了队列的第一个元素。
接下来,添加优先级为1的Ben,Ben的优先级高于John,所以在John前面;Ben的优先级和Jack相同,但Ben在Jack之后添加,所以Ben在Jack后面。
四、循环队列——击鼓传花
在这个游戏中,孩子们围成一个圆圈,把花尽快传递给旁边的人,某一时刻传花停止,这个时候花在谁手里,谁就退出圆圈结束游戏,重复这个过程,直到只剩一个孩子(胜者)。
function hotPotato(nameList, num) {
//将得到的名单全都加入队列
var queue = new Queue();
for (var i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i]);
}
var eliminated = ''; //淘汰
while (queue.size() > 1) {
/*
给定一个数字,迭代队列。
从队列开头移除一项,再将其添加到队列末尾。
如果你把花传给了旁边的人,你被淘汰的威胁立刻就解除了。
*/
for (var i = 0; i < num; i++) {
queue.enqueue(queue.dequeue());
}
//一旦传递次数达到给定的数字,拿着花的那个人就被淘汰了
eliminated = queue.dequeue();
console.log(eliminated + '在击鼓传花游戏中被淘汰。');
}
return queue.dequeue();
}
var names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl'];
var winner = hotPotato(names, 7);
console.log('胜利者:' + winner);
输出如下: