javaScript 数据结构之队列

队列,是先进先出(FIFO, First-In-First-Out)的线性表。在具体应用中通常用链表或者数组来实现。队列只允许在后端(称为rear)进行插入操作,在前端(称为front)进行删除操作。
简而言之队列就像日常生活的排队买东西先到的先享受服务。在这里插入图片描述
队列的实现
普通的队列常用的有以下几个方法:

enqueue 向队列尾部添加一个(或多个)新的项
dequeue 移除队列的第一(即排在队列最前面的)项,并返回被移除的元素
head 返回队列第一个元素,队列不做任何变动
tail 返回队列最后一个元素,队列不做任何变动
isEmpty 队列内无元素返回true,否则返回false
size 返回队列内元素个数
clear 清空队列
class Queue {
  constructor() {
    this._items = [];
  }

  enqueue(item) {
    this._items.push(item);
  }

  dequeue() {
    return this._items.shift();
  }

  head() {
    return this._items[0];
  }

  tail() {
    return this._items[this._items.length - 1];
  }

  isEmpty() {
    return !this._items.length;
  }

  size() {
    return this._items.length;
  }

  clear() {
    this._items = [];
  }
}

同样地,参照上一篇栈的实现方法,我们也可以优化队列的代码:

let Queue = (function () {
	const item = new WeakMap();
	class Queue {
		constructor () {
			item.set(this,[])
		}

		enqueue(element) {
			let q = item.get(this);
			q.push(element);
		}
		
		dequeue() {
			let q = item.get(this);
			let r = q.shift();
			return r;
		}
		
		//其他方法
	}
	return Queue
})()

这样外界就不能访问私有属性item,确保只能使用我们提供的api来操作队列

优先队列
队列大量应用在计算机科学以及我们的生活中,我们在之前话题中实现的默认队列也有一些修改版本。其中一个修改版就是优先队列。元素的添加和移除是基于优先级的。一个现实中的例子是医院的(急诊科)候诊室。医生会优先处理病情比较严重的患者。通常,护士会鉴别分类,根据患者病情的严重程度放号。

function PriorityQueue() {
	let items = [];
	function QueueElement (element, priority){ // {1}
		this.element = element;
		this.priority = priority;
	}
	this.enqueue = function(element, priority){
		let queueElement = new QueueElement(element, priority);
		let added = false;
		for (let i=0; i<items.length; i++){
			if (queueElement.priority < items[i].priority){ // {2}
				items.splice(i,0,queueElement); // {3}
				added = true;
				break; // {4}
			}
		}
		if (!added){
			items.push(queueElement); //{5}
		}
	};
	this.print = function(){
		for (let i=0; i<items.length; i++){
			console.log(`${items[i].element} -${items[i].priority}`);
		}
	};
//其他方法和默认的Queue实现相同
}

循环队列
另一个修改版的队列实现,就是循环队列。循环队列的一个例子就是击鼓传花游戏( Hot
Potato)。在这个游戏中,孩子们围成一个圆圈,把花尽快地传递给旁边的人。某一时刻传花停止,这个时候花在谁手里,谁就退出圆圈结束游戏。重复这个过程,直到只剩一个孩子(胜者)。

function hotPotato(nameList, num) {
  let queue = new Queue(); // {1}
  for (let i = 0; i < nameList.length; i++) {
    queue.enqueue(nameList[i]); // {2}
  }
  let eliminated = '';
  while (queue.size() > 1) {
    for (let i = 0; i < num; i++) {
      queue.enqueue(queue.dequeue()); // {3}
    }
    eliminated = queue.dequeue(); // {4}
    console.log(eliminated + ' 在击鼓传花游戏中被淘汰.');
  }
  return queue.dequeue(); // {5}
}


const arr = ["小刘","小王","小李","bob","lilly","cindy"];
console.log("最终胜利者是:"+hotPotato(arr,3))

打印结果为:

bob 在击鼓传花游戏中被淘汰.
小王 在击鼓传花游戏中被淘汰.
小刘 在击鼓传花游戏中被淘汰.
小李 在击鼓传花游戏中被淘汰.
cindy 在击鼓传花游戏中被淘汰.
最终胜利者是:lilly

循环队列还有一个变式应用——约瑟夫环:
有一个数组a[100]存放0~99;要求每隔两个数删掉一个数,到末尾时循环至开头继续进行,求最后一个被删掉的数。

const arr_100 = Array.from({ length: 100 },(item,index)=>index)
// 创建一个长度为100的数组
function delRing(list) {
  const queue = new Queue();
  list.forEach(e => { queue.enqueue(e); });
  
  let index = 0;
  while (queue.size() !== 1) {
    const item = queue.dequeue();
    index += 1;
    if (index % 3 !== 0) {
      queue.enqueue(item);
    }
  }

  return queue.tail();
}

console.log(delRing(arr_100)); 

队列还可以用来生成斐波那契数列

function fibonacci(n) {
    const queue = new Queue();
    queue.enqueue(1);
    queue.enqueue(1);
    
    let index = 0;
    while(index < n - 2) {
        index += 1;
        // 出队列一个元素
        const delItem = queue.dequeue();
        // 获取头部值
        const headItem = queue.head();
        const nextItem = delItem + headItem;
        queue.enqueue(nextItem);
    }
    
    return queue.tail();
}

console.log(fibonacci(9)); // 34
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值