队列介绍
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
Queue 类定义和一些测试代码
function Queue() { this.dataStore=[]; this.enqueue=enqueue; this.dequeue=dequeue; this.front=front; this.back=back; this.toString=toString; this.isEmpty=isEmpty; } //向队尾添加一个元素 function enqueue(ele) { this.dataStore.push(ele); } //删除队首的元素 function dequeue() { return this.dataStore.shift(); } function front() { return this.dataStore[0]; } function back() { return this.dataStore[this.dataStore.length-1]; } function toString() { let retStr=""; for(let i=0;i<this.dataStore.length;i++){ retStr +=this.dataStore[i]+"\n"; } return retStr; } function isEmpty() { if(this.dataStore.length<=0){ return true; }else{ return false; } } //测试 let q=new Queue(); q.enqueue('Tom'); q.enqueue('Jack'); 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());
队列的使用
1)方块舞的舞伴分配问题:使用队列来模拟跳方块舞的人。当 男男女女来到舞池,他们按照自己的性别排成两队。当舞池中有地方空出来时,选两个队 列中的第一个人组成舞伴。他们身后的人各自向前移动一位,变成新的队首。当一对舞伴 迈入舞池时,主持人会大声喊出他们的名字。当一对舞伴走出舞池,且两排队伍中有任意 一队没人时,主持人也会把这个情况告诉大家。
把跳方块舞的男男女女的姓名储存在一个dancers.txt文件中,引入node fs模块。
dancers.txt文件数据:
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
//舞者信息存储在Dancer对象中 function Dancer(name,sex) { this.name=name; this.sex=sex; } //从dancers.txt中读取舞者信息 function getDanners(males,females) { let names=fs.readFileSync('dancers.txt','utf-8').split("\n"); for(let i=0;i<names.length;i++) { names[i]=names[i].trim();//去除空格 } for(let i=0;i<names.length;i++) { let dancer=names[i].split(" "); let sex=dancer[0]; let name=dancer[1]; if (sex=="F") { females.enqueue(new Dancer(name,sex)); }else{ males.enqueue(new Dancer(name,sex)); } } } //配对 function dance(males,females) { console.log("The dance partners are:\n"); while(!females.isEmpty()&&!males.isEmpty()){ let person=females.dequeue(); console.log("Females dancer is:"+person.name); person=males.dequeue(); console.log("Males dancer is:"+person.name); } } //测试 let maleDancers=new Queue(); let femaleDancers=new Queue(); getDanners(maleDancers,femaleDancers); console.log("maleDancers:",maleDancers); console.log("femaleDancers:",femaleDancers); dance(maleDancers,femaleDancers); if (!femaleDancers.isEmpty()) { console.log(femaleDancers.front().name+"is waiting to dance"); } if (!maleDancers.isEmpty()) { console.log(maleDancers.front().name+"is waiting to dance"); }
2)使用队列对数据进行排序 使用一组队列来模拟基数排序.对于0~99的数字,基数排序将数据集扫描两次。第一次按个位上的数字进行排序,第二次按十位上的数字进行排序。每个数字根据对应位上的数值被分在不同的盒子里。假设有如下数字:
91, 46, 85, 15, 92, 35, 31, 22
经过基数排序第一次扫描之后,数字被分配到如下盒子中:
Bin 0:
Bin 1: 91, 31
Bin 2: 92, 22
Bin 3:
Bin 4:
Bin 5: 85, 15, 35
Bin 6: 46
Bin 7:
Bin 8:
Bin 9:
根据盒子的顺序,对数字进行第一次排序的结果如下:
91, 31, 92, 22, 85, 15, 35, 46
然后根据十位上的数值再将上次排序的结果分配到不同的盒子中:
Bin 0:
Bin 1: 15
Bin 2: 22
Bin 3: 31, 35
Bin 4: 46
Bin 5:
Bin 6:
Bin 7:
Bin 8: 85
Bin 9: 91, 92
最后,将盒子中的数字取出,组成一个新的列表,该列表即为排好序的数字:
15, 22, 31, 35, 46, 85, 91, 92
//将数字分配到对应队列 function distribute(nums,queues,n,digit) { for(let i=0;i<n;i++){ if(nums[i]==""|| !nums[i]){ continue; } if(digit==1){ queues[nums[i]%10].enqueue(nums[i]);//按个位排列 }else{ queues[Math.floor(nums[i]/10)].enqueue(nums[i]);//按十位排列 } } } //从队列中收集数字 function collect(queues,nums){ let i=0; for(let digit=0;digit<10;digit++){ while(!queues[digit].isEmpty()){ nums[i++]=queues[digit].dequeue(); } } } //展示数组内容 function dispArray(arr){ for(let i=0;i<arr.length;i++){ console.log(arr[i]+" "); } } //测试 let queues=[]; for(let i=0;i<10;i++){ queues[i]=new Queue(); } let nums=[]; for(let i=0;i<10;i++){ nums[i]=Math.floor(Math.floor(Math.random()*101)); } //nums=[91,46,85,15,92,35,31,22]; console.log("Before redix sort:"); dispArray(nums); distribute(nums,queues,10,1); collect(queues,nums); distribute(nums,queues,10,10); collect(queues,nums); console.log("After redix sort:"); dispArray(nums);
3)医院急诊科(Emergency Department)的候诊室,就是一个采取优先队列的例子。 当病人进入候诊室时,分诊护士会评估患者病情的严重程度,然后给一个优先级代码。高优先级的患者先于低优先级的患者就医,同样优先级的患者按照先来先服务的顺序就医。
//定义患者对象 function Patient(name,code) { this.name=name; this.code=code; } //重新定义dequeue方法,code小的元素优先级高 function dequeue() { let priority=0; for(let i=0;i<this.dataStore.length;i++){ if(this.dataStore[i].code<this.dataStore[priority].code){ priority=i; } } return this.dataStore.splice(priority,1); } //重新定义toString方法 function toString(){ let retVar=""; for(let i=0;i<this.dataStore.length;i++){ retVar +=this.dataStore[i].name+" code:" +this.dataStore[i].code+"\n"; } return retVar; } let ed=new Queue(); let p=new Patient('Tom',5); 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()); while(!ed.isEmpty()) { let seen=ed.dequeue(); console.log("Patient being treated:"+seen[0].name); console.log("Patient waiting to be seen:") console.log(ed.toString()); }
队列练习
1.修改 Queue 类,形成一个Deque类。这是一个和队列类似的数据结构,允许从队列两端添加和删除元素,因此也叫双向队列。写一段测试程序测试该类
function Deque() { this.dataStore=[]; this.push_front=push_front; this.push_back=push_back; this.pop_front=pop_front; this.pop_back=pop_back; this.front=front; this.back=back; this.toString=toString; this.isEmpty=isEmpty; } //向队首添加一个元素 function push_front(ele) { this.dataStore.unshift(ele); } //向队尾添加一个元素 function push_back(ele) { this.dataStore.push(ele); } //删除队首的元素 function pop_front() { return this.dataStore.shift(); } //删除队尾的元素 function pop_back() { return this.dataStore.pop(); } function front() { return this.dataStore[0]; } function back() { return this.dataStore[this.dataStore.length-1]; } function toString() { let retStr=""; for(let i=0;i<this.dataStore.length;i++){ retStr +=this.dataStore[i]+"\n"; } return retStr; } function isEmpty() { if(this.dataStore.length<=0){ return true; }else{ return false; } } //测试 let d=new Deque(); d.push_back('Tom'); d.push_back('Jack'); d.push_front('Divid'); d.push_front('White'); console.log(d.toString()); d.pop_front(); console.log(d.toString()); d.pop_back(); console.log(d.toString());
2.使用前面完成的Deque类来判断一个给定单词是否为回文。
function isPalindrome(word) { let d=new Deque(); for(let i=0;i<word.length;i++){ d.push_front(word[i]); } let p=new Deque(); for(let i=0;i<word.length;i++){ p.push_back(word[i]); } if(d.dataStore.join("")==p.dataStore.join("")){ return true; }else{ return false; } } let word="aba"; let flag=isPalindrome(word.split("")); console.log(flag);