JavaScript数据结构及算法---队列和双端队列

前言

我们的孤独就像天空中漂浮的城市,仿佛是一个秘密,却无从述说。——《天空之城》

我们认识的队列

今天我们聊一聊数据结构中比较重要的部分—队列。队列的数据结构同栈相同,只是遵循的原则不同,栈遵循先进后出,而队列是遵循先进先出原则的一组有序的项。如下,我们创建一个队列:

class Quene {
	constructor (){
		this.count = 0
		this.lowestCount = 0
		this.items = {}
	}
}

这上面还是一个基本的队列结构,接下来,我们需要用到一些方法来完善队列。
enqueue(element): 向队列尾部添加一个新的项
dequeue():移除对垒的第一项
peek():返回队列中第一个元素并返回被移除的元素
isEmpty():如果队列中不包含任何元素,返回true,否则返回false
size(): 返回队列包含的元素个数

向队列里面添加元素

首先我们来实现enqueue(elements)方法,新的项只能添加到队尾

enqueue(element){
	this.items[this.count] = element
	this.count ++
}
从队列中移除元素

接下来实现dequeue()方法,由于队列遵循先进先出原则,先添加的项也是最先被移除的。

dequeue(){
	if(this.isEmpty()){
		return undefined
	}
	const result = this.items[this.lowestCount]
	delete this.items[this.lowestCount]
	this.lowestCount ++
	return result 
}
查看队列头元素

我们来实现辅助方法,peek()方法的实现。

	peek(){
		if(this.isEmpty()){
			return undefined
		}
		return this.items[this.lowestCount]
	}
检查队列是否为空和获取它的长度
检查队列是否为空
	isEmpty(){
		return this.count - this.lowestCount === 0 
	}
计算队列中有多少个元素
	size(){
		return this.count - this.lowestCount
	}
清空队列
	clear(){
		this,items = {}
		this.count = 0
		this.lowestCount = 0
	}
创建toString方法
	toString(){
		if (this.isEmpty()){
			return ''
		}
		let objString = `${this.items[this.lowestCount]}`
		for(let i = this.lowestCount,max = this.count; i < max; i ++){
			objString = `${objString},${this.items[i]}`
		}
		return objString
	}

构建toString方法,方便我们参看队列中的值。

双端队列数据结构

双端队列是一种允许我们同时从前端和后端添加和移除元素的特殊队列。下面我们可以简单的实现一个双端队列:

	class Deque{
		constructor(){
			this.count = 0
			this.lowestCount = 0
			this.items = {}
		}
	}

双端队列是一种特殊的队列,其构造函数中的部分代码和队列相同,包括内部属性和以下方法:isEmpty,clear,size和toString
由于双端队列允许两端添加和移除元素,还会有几个方法:
addFront(element): 该方法在双端队列前端添加新的元素
addBack(element): 该方法在双端队列后端添加新的方法(实现与Queue类中的enqueue方法相同)
removeFront(): 该方法会从双端队列前端移除一个元素(实现与Queue类中的dequeue方法相同)
removeBack(): 该方法会从双端队列后端移除一个元素(实现与Stack类中的pop方法相同)
peekFrount(): 该方法会返回双端队列的第一个元素(实现与Queuek类中的peek方法相同)
peekBack(): 该方法会返回双端队列的最后一个元素(实现与Stack类中的peek方法相同)

向双端队列的前端添加元素
addFront(element){
	if(this.isEmpty()){
		this.addBack()
	}else if (this.lowestCount > 0){
	this.lowestCount -- 
	this.items[this.lowestCount] = element
	}else{
	for(let i = this.count; i > 0 ; i--){
		this.items[i] = this.items[i-1]
		}
	this.count ++ 
	this.lowestCount = 0
	this.items[0] = element
	}
}

双端队列中较为复杂的就是向双端队列中添加元素,添加元素时需要考虑以下三种情况:
1,当这个队列是空的时候,直接调用向后端添加元素的方法。

2.当一个元素已经被双端队列的前端移除,也就是说lowestCount属性大于1。这种情况下,我们只需要改变lowestCount的值,并将添加的元素放在对应的位置上就可以了。

3.当lowestCount为0的情况,我们可以设置一个负值的键,将队列的第一个位置通过一次位移腾出空位来。然后将新的元素添加到置空的位置上,一旦元素被添加进来,那么count的数量增加并且lowestCount的值始终为0,因为双端队列最开始就没有从前端移除元素。

应用队列和双端队列解决算法问题

循环队列—击鼓传花游戏

在击鼓传花游戏中,孩子们围成一个圆圈,把花尽快传递给旁边的人。某一时刻传花停止,这个时候花落在谁的手里,谁就退出圆圈,结束游戏,直到只剩一个孩子。

	function hotPotato(elementsList, num) {
		const queue = new Queue()
		const elimitatedList = []
		for( let i = 0; i < elementsList.length; i++ ){
			queue.enqueue(elementsList[i])
		}
		// 利用while循环队列
		while (queue.size() > 1){
			for ( let i = 0; i < num; i ++){
				// 从队列开头移除一项,再将其添加到队列末尾,一旦达到了指定的传递次数,拿着花的那个人淘汰
				queue.enqueue(queue.dequeue())
				}
				elimitatedList.push(queue.dequeue())
			}
		}
		return {
			eliminated: elimiatedList // 被淘汰的人,从最开始淘汰到最后淘汰的
			winnder: queue.dequeue()// 最后剩下的人
		}
	}
双端队列— 回文检查器

回文是正反都能读通的单词,词组,数或一系列字符的序列
使用双端队列是最容易解决判断是否是回文的

function palindromeChecker(aString){
	if (aString === undefined || aString === null || (aString !== null && aString.length === 0)) return false
	const dequeue = new Deque()
	const lowerString = aString.toLocaleCase().split(' ').join('')
	let isEqual = true
	let firstChar, lastChar
	while(deque.size > 1 && isEqual){
		for(let i = 0; i < lowerString.length; i ++){
		firstChar = deque.removeFront()
		lastChar = deque.removeBack()
		if(firstChat !== lastChar){
			isEqual = false
		}
	}
	}
	return isEqual
}

通过循环组合的方式,我们可以很容易判断一个字符串是否为回文

总结

今天关于队列的相关内容就到这里,我们下节聊聊!

你可能感兴趣的:(队列,数据结构)