前言
阅读本文前,建议阅读 数据结构与算法---JS与栈
队列数据结构
队列是遵循先进先出(FIFO)原则的一组有序的项。
队列在最尾添加元素,在顶部移除元素。
在日常生活中,排队买票、在计算机中,打印队列都是队列的例子。
创建队列
我们用对象来做队列类的数据存储类型。
class Queue {
#_headKey=0;
#_endKey=0;
#_items={}
}
定义一些队列的方法
向队列添加元素
enqueue(ele) {
this.#_items[this.#_endKey] = ele;
this.#_endKey +=1;
}
返回队列长度
由上面的图可知
size() {
return this.#_endKey - this.#_headKey
}
检验队列是否为空
isEmpty() {
return this.size() === 0
}
从队列里移除元素并返回它
其实就是移除队列里第一个
unenqueue() {
// 为空的话返回undefined
if (this.isEmpty()) {
return undefined
}
const result = this.#_items[this.#_headKey];
delete this.#_items[this.#_headKey];
// 起始指针向后移
this.#_headKey +=1;
return result
}
清空队列
clear() {
while (!this.isEmpty()) {
this.unenqueue()
}
}
创建toString方法
toString() {
if (this.isEmpty()) {return ''};
let objString = '';
for (let i = this.#_headKey;i<this.#_endKey;i++) {
objString+=`[键名:${i}键值:${this.#_items[i]}]`
}
return objString
}
双端队列
双端队列是一种同时遵守了先进先出和后进先出原则,队列和栈相结合的数据结构。
定义
我们储存两个私参 双端队列第一个(最下面)元素的下标和队列的末尾空索引(也就是上文的endKey)
class Deque {
// 队列头部下标
#frontKey =0;
#endKey=0;
#items={};
}
取出双端队列的头部元素
peekFront(){
if (this.isEmpty()) {
return undefined;
}
return this.#items[this.#frontKey];
}
取出双端队列的尾部元素
peekBack() {
if (this.isEmpty()) {
return undefined;
}
return this.#items[this.#endKey - 1];
}
双端队列的长度
size() {
return this.#endKey- this.#frontKey;
}
双端队列是否为空
isEmpty() {
return this.size() === 0;
}
尾部加入元素
addEnd(element) {
this.#items[this.#endKey] = element;
this.#endKey++;
}
头部出列
remove_Front(){
// 为空的话返回undefined
if (this.isEmpty()) {
return undefined
}
const result = this.#items[this.#frontKey];
delete this.#items[this.#frontKey];
this.#frontKey +=1;
return result
}
尾部出列
remove_end(){
if (this.isEmpty()) {
return undefined
}
this.#endKey -=1;
const result = this.#items[this.#endKey];
delete this.#items[this.#endKey];
return result
}
清除队列
clear() {
this.#items = {};
this.#frontKey = 0;
this.#endKey = 0;
}
头部加入元素
分几种情况:
①空的队列,调用addEnd方法
②#frontKey>=1(即发生过前出栈)的情况,此时要往frontKey前放置新入列的元素
③#frontKey为0,且队列长度不为空的情况,这个时候我们要往索引0前插入元素,就会产生负数的索引。这时需要所有下标往右移动。我们可以从最后一位开始迭代所有的值,每一个位置为其重新赋值(赋值为原本索引-1的值)、等所有的元素完成移动后,0位(第一位)将被空闲出来,此时我们再进行插入。
addFront(element) {
if (this.isEmpty()) {
this.addEnd(element);
} else if (this.#frontKey > 0) {
this.#frontKey--;
this.#items[this.#frontKey] = element;
} else {
for (let i = this.#endKey; i > 0; i--) {
this.#items[i] = this.#items[i - 1];
}
this.#endKey++;
this.#items[0] = element;
}
}
toString
toString() {
if (this.isEmpty()) {return ''};
let objString = '';
for (let i = this.#frontKey;i<this.#endKey;i++) {
objString+=`[键名:${i}键值:${this.#items[i]}]`
}
return objString
}
实例化双端队列
class Deque {
// 队列头部下标
#frontKey =0;
#endKey=0;
#items={};
peekFront(){
if (this.isEmpty()) {
return undefined;
}
return this.#items[this.#frontKey];
}
peekBack() {
if (this.isEmpty()) {
return undefined;
}
return this.#items[this.#endKey - 1];
}
size() {
return this.#endKey- this.#frontKey;
}
isEmpty() {
return this.size() === 0;
}
addEnd(element) {
this.#items[this.#endKey] = element;
this.#endKey++;
}
remove_Front(){
// 为空的话返回undefined
if (this.isEmpty()) {
return undefined
}
const result = this.#items[this.#frontKey];
delete this.#items[this.#frontKey];
this.#frontKey +=1;
return result
}
remove_end(){
if (this.isEmpty()) {
return undefined
}
this.#endKey -=1;
const result = this.#items[this.#endKey];
delete this.#items[this.#endKey];
return result
}
clear() {
this.#items = {};
this.#frontKey = 0;
this.#endKey = 0;
}
addFront(element) {
if (this.isEmpty()) {
this.addEnd(element);
} else if (this.#frontKey > 0) {
this.#frontKey--;
this.#items[this.#frontKey] = element;
} else {
for (let i = this.#endKey; i > 0; i--) {
this.#items[i] = this.#items[i - 1];
}
this.#endKey++;
this.#items[0] = element;
}
}
toString() {
if (this.isEmpty()) {return ''};
let objString = '';
for (let i = this.#frontKey;i<this.#endKey;i++) {
objString+=`[键名:${i}键值:${this.#items[i]}]`
}
return objString
}
}
var deque = new Deque()
用队列模拟击鼓传花游戏场景
我们要关注的是两个地方:
①学生数组
②拍手N次
因为击鼓传花会一直来回循环。我们用队列来模拟场景
class Queue {
#_headKey=0;
#_endKey=0;
#_items={}
enqueue(ele) {
this.#_items[this.#_endKey] = ele;
this.#_endKey +=1;
}
size() {
return this.#_endKey - this.#_headKey
}
isEmpty() {
return this.size() === 0
}
nenqueue() {
// 为空的话返回undefined
if (this.isEmpty()) {
return undefined
}
const result = this.#_items[this.#_headKey];
delete this.#_items[this.#_headKey];
// 起始指针向后移
this.#_headKey +=1;
return result
}
peek() {
if (this.isEmpty()) {
return undefined;
}
return this.#_items[this.#_endKey - 1];
}
}
const students = ['学生A','学生B','学生C','学生D'];
// 击鼓三下停止击鼓
const stopTimes = 3;
const game =(students,stopTimes)=>{
const queueS = new Queue();
// 创建学生队列
for (var i = 0; i < students.length;i++){
queueS.enqueue(students[i]);
}
// 淘汰到最后一个人即不进行击鼓
while (queueS.size()>1) {
// 开始击鼓
for (var ii =0;ii<stopTimes;ii++){
if (ii === stopTimes-1) {
// 击鼓结束
console.log('击鼓结束');
const result = queueS.nenqueue();
console.log('本轮被淘汰的是'+result);
}else {
const result = queueS.nenqueue();
// 击鼓继续
console.log('花当前在' + result + '手里');
queueS.enqueue(result);
}
}
}
return queueS.peek()
}
运行:
game(students,stopTimes )
VM2349:22花当前在学生A手里
VM2349:22 花当前在学生B手里
VM2349:16 击鼓结束
VM2349:18 本轮被淘汰的是学生C
VM2349:22 花当前在学生D手里
VM2349:22 花当前在学生A手里
VM2349:16 击鼓结束
VM2349:18 本轮被淘汰的是学生B
VM2349:22 花当前在学生D手里
VM2349:22 花当前在学生A手里
VM2349:16 击鼓结束
VM2349:18 本轮被淘汰的是学生D
=>'学生A'
用双端队列解决回文问题
什么是回文
回文指正读和反读都相同的字符序列。比如“abba”,“12321”等。
现在我们要写一个判断字符串是否为回文的方法
图解判断逻辑
代码
class Deque {
// 队列头部下标
#frontKey =0;
#endKey=0;
#items={};
size() {
return this.#endKey- this.#frontKey;
}
remove_Front(){
// 为空的话返回undefined
if (this.isEmpty()) {
return undefined
}
const result = this.#items[this.#frontKey];
delete this.#items[this.#frontKey];
this.#frontKey +=1;
return result
}
remove_end(){
if (this.isEmpty()) {
return undefined
}
this.#endKey -=1;
const result = this.#items[this.#endKey];
delete this.#items[this.#endKey];
return result
}
isEmpty() {
return this.size() === 0;
}
addEnd(element) {
this.#items[this.#endKey] = element;
this.#endKey++;
}
}
function isPalindrome(str) {
if (!str) {return false}
if (str.length === 1) {return true}
const deque = new Deque();
str = str.toLowerCase();
const strArr = str.split('');
for(var i = 0; i<strArr.length;i++){
deque.addEnd(strArr[i])
}
var result = true;
while(deque.size() > 1) {
const comapreA = deque.remove_Front();
const comapreB = deque.remove_end();
console.log('开始比较')
if (comapreA !== comapreB) {
result = false;
console.log('比较结束')
break;
}
}
return result;
}