javascript数据结构-队列和双端队列

1. 队列数据结构

队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾。

1.1 创建队列

首先需要一个用于存储队列中元素的数据结构。可以使用数组,但是为了写出一个在获取元素时更高效的数据结构,将使用一个对象来存储元素。会发现Queue类和Stack类非常类似,只是添加和移除元素的原则不同。

export default class Queue {
  constructor() {
    this.count = 0;
    this.lowestCount = 0;
    this.items = {};
  }

  // enqueue(element(s)): 向队列尾部添加一个或多个新的项
  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(): 如果队列中不包含任何元素,返回true,否则返回false
  isEmpty() {
    return this.count - this.lowestCount === 0
  }

  // size(): 返回队列包含的元素个数,与数组的length属性类似
  size() {
    return this.count - this.lowestCount
  }

  // 清理队列
  clear() {
    this.count = 0;
    this.lowestCount = 0
    this.items = {}
  }

  // 创建一个toString方法
  toString() {
    if(this.isEmpty()) {
      return ""
    }
    let objString = `${this.items[this.lowestCount]}`
    for(let i = this.lowestCount + 1; i < this.count; i++) {
      objString = `${objString}, ${this.items[i]}`
    }

    return objString
  }

}
1.2 使用Queue类
// Queue:首先是实例化刚刚创建的Queue类
const queue = new Queue()
console.log(queue.isEmpty())  //true,队列为空

// 向队列添加三个元素
queue.enqueue('John')
queue.enqueue('Jack')
queue.enqueue('ytllll')

console.log(queue.toString()) //John, Jack, ytllll

queue.enqueue("Kotoko")
console.log(queue.toString()) //John, Jack, ytllll, Kotoko
console.log(queue.size()) //4
console.log(queue.isEmpty()) //false

// 删除前两个元素
queue.dequeue()
queue.dequeue()
console.log(queue.toString()) //ytllll, Kotoko

2. 双端队列数据结构

双端队列(deque,或称double-ended queue)是一种运行我们同时从前端和后端添加和移除元素的特殊队列。
双端队列在现实生活中的例子有电影院、餐厅中排队的队伍等。

2.1 创建Deque类
export default class Deque {
  constructor() {
    this.count = 0;
    this.lowestCount = 0;
    this.items = {};
  }

  // 该方法在双端队列前端添加新的元素
  addFront(element) {
    if(this.isEmpty()) {
      this.addBack(element)
    }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
    }
  }

  // 向双端队列后端添加新的元素
  addBack(element) {
    this.items[this.count] = element
    this.count++
  }

  // 该方法会从双端队列前端移除第一个元素并返回
  removeFront() {
    if(this.isEmpty()) {
      return undefined
    }
    const result = this.items[this.lowestCount]
    delete this.items[this.lowestCount]
    this.lowestCount++
    return result
  }

  // 该方法会从双端队列后端移除第一个元素并返回
  removeBack() {
    if (this.isEmpty()) {
      return undefined
    }
    this.count--
    const result = this.items[this.count]
    delete this.items[this.count]
    return result
  }

  // 该方法会从双端队列前端返回第一个元素
  peekFront() {
    if(this.isEmpty()) {
      return undefined
    }
    return this.items[this.lowestCount]
  }

  // 该方法会从双端队列后端返回第一个元素
  peekBack() {
    if (this.isEmpty()) {
      return undefined
    }
    return this.items[this.count - 1]
  }

  // isEmpty(): 如果队列中不包含任何元素,返回true,否则返回false
  isEmpty() {
    return this.count - this.lowestCount === 0
  }

  // size(): 返回队列包含的元素个数,与数组的length属性类似
  size() {
    return this.count - this.lowestCount
  }

  // 清理队列
  clear() {
    this.count = 0;
    this.lowestCount = 0
    this.items = {}
  }

  // 创建一个toString方法
  toString() {
    if(this.isEmpty()) {
      return ""
    }
    let objString = `${this.items[this.lowestCount]}`
    for(let i = this.lowestCount + 1; i < this.count; i++) {
      objString = `${objString}, ${this.items[i]}`
    }

    return objString
  }
}
2.2 使用Deque类
// Deque:实例化Deque类
const deque = new Deque()
console.log(deque.isEmpty()) //true
// 向双端队列后端加入两个元素
deque.addBack('John')
deque.addBack('Jack')
console.log(deque.toString()) //John, Jack
// 向双端队列前端加入两个元素
deque.addFront('ytllll')
deque.addFront('Kotoko')
console.log(deque.toString()) //Kotoko, ytllll, John, Jack
console.log(deque.size()) //4
console.log(deque.isEmpty()) //false
// 取队列的第一个元素
console.log(deque.peekFront()) //Kotoko
// 取队列的最后一个元素
console.log(deque.peekBack()) //Jack
// 移除队列第一个元素
deque.removeFront()
console.log(deque.toString()) //ytllll, John, Jack
// 移除队列最后一个元素
deque.removeBack()
console.log(deque.toString()) //ytllll, John

3. 使用队列和双端队列来解决问题

3.1 循环队列——击鼓传花游戏

在这个游戏中,孩子们围成一个圆圈,把花尽快的传递给旁边的人。某一时刻传花停止,这个时候花在谁手里,谁就退出圆圈,结束游戏。重复这个过程,直到只剩下一个孩子(胜者)。
接下来使用队列来实现这个游戏:

function hotPotato(elementList, num) {
  // 定义一个空队列
  const queue = new Queue()
  // 定义一个保存淘汰者的数组
  const eliminatedList = []

  for(let i = 0; i < elementsList.length; i++) {
    // 遍历传入的elementsList数组,逐个传入queue队列
    queue.enqueue(elementsList[i])
  }

  while(queue.size() > 1) {
    for(let i = 0; i < num; i++) {
      // 从队列头开始,移除一项再将返回值添加会队列,模拟一次传递
      queue.enqueue(queue.dequeue) 
    }
    // 循环结束,传到第num次的人淘汰,并加入数组
    eliminatedList.push(queue.dequeue)
  }

  return {
    eliminated: eliminatedList,
    // 队列中剩下的最后一个人就是胜者
    winner: queue.dequeue()
  }

}

const names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl']
const result = hotPotato(names, 7)
result.eliminated.forEach(name => {
  console.log(`${name}在击鼓传花游戏中被淘汰`)
})
console.log(`胜利者: ${result.winner}`)

以上函数输出如下:
Camila在击鼓传花游戏中被淘汰
Jack在击鼓传花游戏中被淘汰
Carl在击鼓传花游戏中被淘汰
Ingrid在击鼓传花游戏中被淘汰
胜利者:John
下图模拟了这个输出过程:
在这里插入图片描述

3.2 回文检查器

维基百科对回文的解释:回文是正反都能读通的单词、词组、数或一系列字符的序列,例如madam或racecar。
有不同的算法可以检查一个词组或字符串是否回文。最简单的方式是将字符串反向排列并检查它和原字符串是否相同。如果两者相同,那么它就是一个回文。我们也可以用栈来完成,但是利用数据结构来解决这个问题最简单方法是使用双端队列。
下面使用一个双端队列来解决这个问题:

function palindromeChecker(aString) {
  if(aString === undefined || aString === null || (aString !== null && aString.length === 0)) {
    return false
  }
  const deque = new Deque()
  const lowerString = aString.toLocaleLowerCase().split(' ').join('')
  let isEqual = true
  let firstChar, lastChar

  for(let i = 0; i < lowerString.length; i++) {
    deque.addBack(lowerString.charAt(i))
  }

  while(deque.size() > 1 && isEqual) {
    firstChar = deque.removeFront()
    lastChar = deque.removeBack()
    if(firstChar !== lastChar) {
      isEqual = false
    }
  }

  return isEqual
}

console.log('a', palindromeChecker('a'));
console.log('aa', palindromeChecker('aa'));
console.log('kayak', palindromeChecker('kayak'));
console.log('level', palindromeChecker('level'));
console.log('Step on no pets', palindromeChecker('Step on no pets'));

以上输出都是true

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值