JS数据结构与算法 —— 队列(Queue)
- 什么是队列 ?
队列(Queue):它是一种 受限的线性表,先进先出(FIFO)。受限之处在于,它只能在表的 前端 进行 删除 操作,在表的 后端 进行 插入 操作。
- 队列的应用 ?
- 打印队列:有五份文档需要打印,这些文档会按照次序放入到打印队列中,打印会依次从队列中取出文档,优先放入的文档,优先被取出,并且对该文档进行打印,以此类推,直到队列中不再有新的文档。
- 线程队列:在开发中,为了让任务可以并行处理,通常会开启多个线程。但是,我们不能让大量的线程同时运行处理任务,占用资源太多。这个时候,如果有需要开启线程处理的情况,我们就会使用线程队列。线程队列会依次启动线程,并且处理对应任务。(当考虑到线程任务的优先性的时候,可以使用优先队列去完成)
- 队列的实现 ?
队列的实现:可以 基于数组 实现,也可以 基于链表实现(效率更高)。下面先讨论以数组实现的方式,基于链表实现的,后面补充。
// 封装队列类
function Queue(){
// 队列中的属性
this.items = [];
// 队列中的操作
// 1,向队列后端添加元素
Queue.prototype.enqueue = function(el){
this.items.push(el);
}
// 2,删除队列前端元素,并返回
Queue.prototype.dequeue = function(){
return this.items.shift();
}
// 3,返回队列前端元素
Queue.prototype.fork = function(){
return this.items[0];
}
// 4,判断队列是否为空
Queue.prototype.isEmpty = function(){
return this.items.length === 0;
}
// 5.返回队列的长度
Queue.prototype.size = function(){
return this.items.length;
}
// 6,返回字符串形式的队列内容
Queue.prototype.toString = function(){
var resultStr = '';
for(var i=0; i<this.items.length; i++){
resultStr += this.items[i] + ' ';
}
return resultStr;
}
}
// 使用队列
var q = new Queue();
q.enqueue(12);
q.enqueue(34);
q.enqueue(14);
q.enqueue(45);
alert(q);
alert(q.dequeue());
alert(q.fork());
alert(q.isEmpty());
alert(q.size());
alert(q.toString());
- 面试题——击鼓传花 ?
面试题,击鼓传花:有几个朋友围成一圈玩一个游戏。确定一个数字,当某个人喊到该数字的时候,就出局。直到最后剩下的那个人,则为胜利者,问这个胜利者原本的位置在什么地方?(即原来的下标值)
// 队列类的封装
function Queue(){
// 属性
this.items = [];
// 方法
// 1,队列后端插入元素
Queue.prototype.enqueue = function(el){
this.items.push(el);
}
// 2,队列前端删除元素,并返回删除的元素
Queue.prototype.dequeue = function(){
return this.items.shift();
}
// 3,返回队列前端元素,队列不做任何变化
Queue.prototype.fork = function(){
return this.items[0];
}
// 4,判断队列是否为空
Queue.prototype.isEmpty = function(){
return this.items.length === 0;
}
// 5,返回队列的长度
Queue.prototype.size = function(){
return this.items.length;
}
// 6,以字符串的形式返回队列的内容
Queue.prototype.toString = function(){
var resultStr = '';
for(var i=0; i<this.items.length; i++){
resultStr += this.items[i] + ' ';
}
return resultStr;
}
}
// 击鼓传花的函数,有两个参数,nameList是参加游戏的人的姓名;num是确定的游戏数字
function transmit(nameList,num){
// 1,把人全部添加到队列中
// 1.1,新建队列
var q = new Queue();
// 1.2,循环加入队列
for(var i=0; i<nameList.length; i++){
q.enqueue(nameList[i]);
}
// 2,把队列前num-1个元素移除重新添加到队列后端。把队列?num个元素进行队列前端的删除。依次循环,循环跳出条件为,剩下一个人的时候,即队列长度为一的时候。
while(q.size > 1){
// 2.1,将前num-1个元素移动到队列后端
for(var i=0; i<num-1; i++){
q.enqueue(q.dequeue());
}
// 2.2,删除第num个元素
q.dequeue();
}
// 3,队列中剩下一个元素,即为胜利者
return nameList.indexOf(q.fork());
}
// 游戏函数测试
var nameList = ['wuhao','liming','zhousan','yuer','tianwei','sunney'];
alert(transmit(nameList,3));
- 优先级队列 ?
优先级队列:优先级队列,属性不仅有元素内容,也包含元素优先级。可以通过元素优先级,将元素放到正确的位置上。
- 优先级队列的实现 ?
优先级队列的实现 :首先优先级队列属于队列,所以也是可以用数组或者链表去实现它,这里讨论数组的实现思路。优先级队列,和普通队列相比,由于拥有优先级,在队列后端插入元素的时候,要额外的去对比优先级高低,判断合适位置插入。通过可以分为以下三种情况。第一种,队列中没有元素,即首次插入,不用考虑优先级,直接插入即可。第二种,队列非空,插入元素,找到第一个比插入元素优先级大的元素,插入到找出的元素之前即可。第三种,队列非空,遍历后没有比插入元素优先级大的,就插入到队列末尾即可。
// 优先级队列类的封装
function PriorityQueue(){
// 在PriorityQueue中重新创建一个类,可以看作内部类
function QueueItem(element,priority){
this.element = element;
this.priority = priority;
}
// 封装属性
this.items = [];
// 方法
// 1,队列后端插入元素
PriorityQueue.prototype.enqueue = function(element,priority){
// 使用 QueueItem 内部类
var queueItem = new QueueItem(element,priority);
// 队列为空,直接添加元素
if(this.items.length === 0){
this.items.push(queueItem);
}else{
var added = false;
// 队列不为空,找到大于它优先级的元素,插入到它之前
for(var i=0; i<this.items.length; i++){
if(queueItem.priority < this.items[i].priority){
this.items.splice(i,0,queueItem);
added = true;
break;
}
}
// 队列不为空,没有找到大于它优先级的元素,插入到队尾
if(!added){
this.items.push(queueItem);
}
}
}
// 2,队列前端删除元素
PriorityQueue.prototype.dequeue = function(){
return this.items.shift();
}
// 3,返回队列前端元素,不改变队列
PriorityQueue.prototype.fork = function(){
return this.items[0];
}
// 4,判断队列是否为空
PriorityQueue.prototype.isEmpty = function(){
return this.items.length === 0;
}
// 5,返回队列的长度
PriorityQueue.prototype.size = function(){
return this.items.length;
}
// 6,以字符串形式返回队列内容
PriorityQueue.prototype.toString = function(){
var resultStr = '';
for(var i=0; i<this.items.length; i++){
resultStr += this.items[i].element +' :' + this.items[i].priority +' ';
}
return resultStr;
}
}
// 优先级队列使用
var pq = new PriorityQueue();
pq.enqueue('wuhao',110);
pq.enqueue('liming',10);
pq.enqueue('zhouyu',80);
pq.enqueue('huwen',200);
alert(pq.toString());