队列是一种列表,不同的是队列只能在队尾插入元素,在队首删除元素。是一种先进先出的数据结构。
队列被用在很多地方,比如提交操作系统执行的一系列进程、打印任务池等,一些仿真系统用队列来模拟银行或杂货队里排队的顾客。
用数组实现队列
// 实现一个队列
function Queue(){
this.dataStore = [];
this.enqueue = enqueue; // 入队
this.dequeue = dequeue; // 出队
this.front = front; // 查看队首元素
this.back = back; // 查看队尾元素
this.toString = toString; // 显示队列所有元素
this.empty = empty; // 判断队列是否为空
this.count = count; // 显示队列中元素的个数
}
function enqueue(element){
this.dataStore.push(element);
}
function dequeue(){
return this.dataStore.shift();
}
function front(){
return this.dataStore[0];
}
function back(){
return this.dataStore[this.dataStore.length - 1];
}
function toString(){
var retStr = "";
for(var i = 0; i < this.dataStore.length; i++){
retStr += this.dataStore[i] + "\n";
}
return retStr;
}
function empty(){
if(this.dataStore.length == 0){
return true;
}else{
return false;
}
}
function count(){
return this.dataStore.length;
}
// 测试队列
var q = new Queue();
q.enqueue("Meredith");
q.enqueue("Cynthia");
q.enqueue("Jennifer");
console.log(q.toString());
q.dequeue();
console.log(q.toString());
console.log("Front of queue: " + q.front());
console.log("Back of queue: " + q.back());
//Meredith
//Cynthia
//Jennifer
//Cynthia
//Jennifer
//Front of queue: Cynthia
//Back of queue: Jennifer
使用队列:方块舞的舞伴分配问题
前面我们提到过,经常用队列模拟排队的人。下面我们使用队列来模拟跳方块舞的人。当男男女女来到舞池,他们按照自己的性别排成两队。当舞池中有地方空出来时,选两个队列中的第一个人组成舞伴。他们身后的人各自向前移动一位,变成新的队首。当一对舞伴迈入舞池时,主持人会大声喊出他们的名字。当一对舞伴走出舞池,且两排队伍中有任意一队没人时,主持人也会把这个情况告诉大家。下面是这个舞会的具体安排:
// 将信息存到一个Dancer对象中
function Dancer(name,sex){
this.name = name;
this.sex = sex;
}
//将舞者信息从文件中读到程序里来
function getDancers(maleDancers,femaleDancers){
var names = [
"F Allison McMillan",
"M Frank Opitz",
"M Mason McMillan",
"M Clayton Ruff",
"F Cheryl Ferenback",
"M Raymond Williams",
"F Jennifer Ingram",
"M Bryan Frazer",
"M David Durr",
"M Danny Martin",
"F Aurora Adney"];
// 将男女放到两个队列中
for(var i = 0;i<names.length;i++){
var dancer = names[i].split(" ");
var sex = dancer[0];
var name = dancer[1];
if(sex == "F"){
femaleDancers.enqueue(new Dancer(name,sex));
}else{
maleDancers.enqueue(new Dancer(name,sex));
}
}
}
// 男女组成舞伴,并宣布结果
function dance(males,females){
console.log("The dance partners are: \n");
while(!females.empty() && !males.empty()){
person = females.dequeue();
console.log("female dancer is: " + person.name);
person = males.dequeue();
console.log(" and the male dancer is: " + person.name);
}
console.log();
}
// 测试程序
var maleDancers = new Queue();
var femaleDancers = new Queue();
getDancers(maleDancers,femaleDancers);
dance(maleDancers,femaleDancers);
//显示等候跳舞的人
if(maleDancers.count()>0){
console.log("There are "+maleDancers.count()+"male dancers waiting to dance.")
}
if(femaleDancers.count()>0){
console.log("There are "+femaleDancers.count()+"female dancers waiting to dance.")
}
// The dance partners are:
// female dancer is: Allison
// and the male dancer is: Frank
// female dancer is: Cheryl
// and the male dancer is: Mason
// female dancer is: Jennifer
// and the male dancer is: Clayton
// female dancer is: Aurora
// and the male dancer is: Raymond
// There are 3male dancers waiting to dance.
使用队列对数据进行排序
队列不仅用于执行现实生活中与排队有关的操作,还可以用于对数据进行排序。计算机刚刚出现时,程序是通过穿孔卡输入主机的,每张卡包含一条程序语句。这些穿孔卡装在一个盒子里,经一个机械装置进行排序。我们可以使用一组队列来模拟这一过程。这种排序技术叫做基数排序。
对于0 ~ 99的数字,基数排序将数据集扫描两次。第一次按个位上的数字进行排序,第二次按十位上的数字进行排序。每个数字根据对应位上的数值被分在不同的盒子里。假设有如下数字:91, 46, 85, 15, 92, 35, 31, 22
根据个位数字,第一次排序后,盒子里的数字顺序如下:
0:
1: 91, 31
2: 92, 22
3:
4:
5: 85, 15, 35
6: 46
7:
8:
9:
根据盒子的顺序,对数字进行第一次排序的结果如下:91, 31, 92, 22, 85, 15, 35, 46
根据十位数字第二次排序后,盒子里的数字顺序如下:
0:
1: 15
2: 22
3: 31, 35
4: 46
5:
6:
7:
8: 85
9: 91,92
最后结果为:15, 22, 31, 35, 46, 85, 91, 92
使用队列代表盒子,可以实现这个算法。我们需要九个队列,每个对应一个数字。将所有队列保存在一个数组中,使用取余和除法操作决定个位和十位。算法的剩余部分将数字加入相应的队列,根据个位数值对其重新排序,然后再根据十位上的数值进行排序,结果为排好序的数字。
// 根据个位或者十位将数字分配到对应的队列中
function distribute(nums,queues,n,digit){
for(var i = 0;i<n;i++){
if(digit == 1){
queues[nums[i]%10].enqueue(nums[i]);
}else{
queues[Math.floor(nums[i]/10)].enqueue(nums[i]);
}
}
}
// 从队列中收集数字
function collect(queues,nums){
var i = 0;
for(var digit = 0;digit<10;++digit){
while(!queues[digit].empty()){
nums[i++] = queues[digit].dequeue();
}
}
}
// 显示数组中的元素
function dispArray(arr){
for(var i = 0;i<arr.length;++i){
console.log(arr[i]+" ");
}
}
// 测试程序
var queues = [];
for(var i = 0;i<10;++i){
queues[i] = new Queue();
}
var nums = [];
for(var i = 0;i<10;++i){
nums[i] = Math.floor(Math.floor(Math.random()*101));
}
console.log("Before radix sort: ");
dispArray(nums);
distribute(nums,queues,10,1);
collect(queues,nums);
distribute(nums,queues,10,10);
collect(queues,nums);
console.log("\n\nAfter radix sort: ");
dispArray(nums);
在一般情况下,从队列中删除的元素,一定是率先人队的元素。但是也有一些使用队列的应用,在删除元素时不必遵守先进先出的约定。这种应用,需要使用一个叫做优先队列的数据结构来进行模拟。
从优先队列中删除元素时,需要考虑优先权的限制。比如医院急诊科的候诊室,就是一个采取优先队列的例子。当病人进入候诊室时,分诊护士会评估患者病情的严重程度,然后给一个优先级代码。高优先级的患者先于低优先级的者就医,同样优先级的患者按照先来先服务的顺序就医。
先来定义存储队列元素的对象,然后再构建我们的优先队列系统:
function Patient(name,code){
this.name = name;
this.code = code;
}
现在需要重新定义 dequeve()方法,使其删除队列中拥有最高优先级的元素。我们规定优先码的值最小的元素优先级最高。新的 dequeue()方法遍历队列的底层存储数组,从中找出优先码最低的元素,然后使用数组的 splice()方法删除优先级最高的元素。新的dequeue()方法定义如下所示:
function dequeue(){
var entry = 0;
for(var i = 0;i<this.dataStore.length;++i){
if(this.dataStore[i].code<this.dataStore[entry].code){
entry = i;
}
}
return this.dataStore.splice(entry,1);
}
dequeue()方法使用简单的顺序查找方法寻找优先级最高的元素(优先码越小优先级越高比如,1比 5 的优先级高)。该方法返回包含一个元素的数组一一从队列中删除的元素。
function toString(){
var retStr = "";
for(var i = 0;i<this.dataStore.length;++i){
retStr += this.dataStore[i].name + " code: "
+ this.dataStore[i].code + "\n";
}
return retStr;
}
// 测试程序
var p = new Patient("Smith",5);
var ed = new Queue();
ed.enqueue(p);
p = new Patient("Jones",4);
ed.enqueue(p);
p = new Patient("Fehrenbach",6);
ed.enqueue(p);
p = new Patient("Brown",1);
ed.enqueue(p);
p = new Patient("Ingram",1);
ed.enqueue(p);
console.log(ed.toString());
var seen = ed.dequeue();
console.log("Patient being treated: "+seen[0].name);
console.log("Patients waiting to be seen: ");
console.log(ed.toString());
// 下一轮
var seen = ed.dequeue();
console.log("Patient being treated: "+seen[0].name);
console.log("Patients waiting to be seen: ");
console.log(ed.toString());
var seen = ed.dequeue();
console.log("Patient being treated: "+seen[0].name);
console.log("Patients waiting to be seen: ");
console.log(ed.toString());