用JavaScript刷leetcode第641题-设计循环双端队列

428 阅读5分钟

一、前言

这题需要一些前置知识,可以先看我的上篇文章 设计循环队列
循环双端队列与普通循环队列的主要区别就是

  • 循环双端队列既可以在队首插入删除,也可以在队尾插入删除,不遵循先进先出。
  • 而普通循环队列只能在队尾插入,队首删除,还是遵循先进先出的。 搞清楚这些后,我将用数组模拟实循环双端队列

二、题目描述:

设计实现双端队列。
你的实现需要支持以下操作:

  • MyCircularDeque(k):构造函数,双端队列的大小为k。
  • insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true。
  • insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true。
  • deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true。
  • deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true。
  • getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1。
  • getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1。
  • isEmpty():检查双端队列是否为空。
  • isFull():检查双端队列是否满了。

示例:

MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1);			        // 返回 true
circularDeque.insertLast(2);			        // 返回 true
circularDeque.insertFront(3);			        // 返回 true
circularDeque.insertFront(4);			        // 已经满了,返回 false
circularDeque.getRear();  				// 返回 2
circularDeque.isFull();				        // 返回 true
circularDeque.deleteLast();			        // 返回 true
circularDeque.insertFront(4);			        // 返回 true
circularDeque.getFront();				// 返回 4

更加详细的描述请看: leetcode连接

三、解题

如果你之前设计过循环队列,这题其实很简单。接下来我将带你逐一实现双端队列需要支持的操作

3.1 MyCircularDeque(k):构造函数,双端队列的大小为k

用数组模拟循环双端队列申请下来的空间,需要两个指针来模拟队列的头尾,还需要一个变量记录空间的最大容量

/**
 * @param {number} k
 */
var MyCircularDeque = function(k) {
  // 队列申请到空间的最大容量
  this.max = k
  // 头
  this.front = 0
  // 尾的后一位, 设计的是这种[front, rear)
  this.rear = 0
  // 队列申请到的空间
  this.queue = new Array(k).fill(null)
}

3.2 insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true

  • 插入操作首先要考虑队列是否满了
  • 队首插入,要考虑两种情况:1、队尾 2、非队尾
  • 非队尾很简单this.front = this.front - 1
  • 队尾 this.front = this.max - 1
/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularDeque.prototype.insertFront = function(value) {
  // 如果队列满了,插入失败
  if(this.isFull()) return false

  // 否则front指针向前移动一位,但是要考虑front 在 0 的位置时
  this.front = this.front === 0 ? this.max - 1 : this.front - 1

  // 插入
  this.queue[this.front] = value

  // 插入成功
  return true
};

3.3 insertLast():将一个元素添加到双端队列尾部。如果操作成功返回 true

  • 先判满
  • 队尾插入,要考虑两种情况:1、队尾 2、非队尾
  • 两种情况可以分开写,也可一行代码搞定分开写的代码,我注释了
/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularDeque.prototype.insertLast = function(value) {
  // 如果队列满了,插入失败
  if(this.isFull()) return false

  // 插入
  this.queue[this.rear] = value

  // 后移
  // this.rear = this.rear === this.max - 1 ? 0 : this.rear + 1
  this.rear = (this.rear + 1) % this.max

  // 插入成功
  return true
};

3.4 deleteFront():从双端队列头部删除一个元素。 如果操作成功返回 true

  • 先判空,判空则删除失败
  • 队头删除,要考虑两种情况:1、队尾 2、非队尾
/**
 * @return {boolean}
 */
MyCircularDeque.prototype.deleteFront = function() {
  // 判空,删除失败
  if(this.isEmpty()) return false
  // 删除
  this.queue[this.front] = null
  // 指针后移
  this.front = (this.front + 1) % this.max
  // 删除成功
  return true
};

3.5 deleteLast():从双端队列尾部删除一个元素。如果操作成功返回 true

  • 先判空,判空则删除失败
  • 队头删除,要考虑两种情况:1、队尾 2、非队尾
/**
 * @return {boolean}
 */
MyCircularDeque.prototype.deleteLast = function() {
  // 判空,删除失败
  if(this.isEmpty()) return false

  // 指针后移
  this.rear = this.rear === 0 ? this.max - 1 : this.rear - 1
  
  // 删除
  this.queue[this.rear] = null
  
  // 删除成功
  return true

};

3.6 getFront():从双端队列头部获得一个元素。如果双端队列为空,返回 -1

  • 判空,则返回-1
  • 返回第一个元素
/**
 * @return {number}
 */
MyCircularDeque.prototype.getFront = function() {
  // 判空
  if(this.isEmpty()) return -1
  // 返回
  return this.queue[this.front]
};

3.7 getRear():获得双端队列的最后一个元素。 如果双端队列为空,返回 -1

  • 判空,则返回-1
  • 返回最后一个元素
  • 注意rear指向的是 尾 的 前一位
/**
 * @return {number}
 */
MyCircularDeque.prototype.getRear = function() {
  // 判空
  if(this.isEmpty()) return -1
  // rear 指的是 尾的前一位, 当指向0 时实际指向尾
  return this.queue[this.rear === 0 ? this.max - 1 : this.rear -1]
};

3.8 isEmpty():检查双端队列是否为空

  • 判空的条件是:front 和 rear 指向同一个位置,并且此位置上的元素是 null
/**
 * @return {boolean}
 */
MyCircularDeque.prototype.isEmpty = function() {
  // front 和 rear 指向同一个位置,并且此位置上的元素是 null
  return (this.front === this.rear) && (this.queue[this.front] === null)
};

3.9 isFull():检查双端队列是否满了

  • 判满的条件是:front 和 rear 指向同一个位置,并且此位置上的元素不是 null
/**
 * @return {boolean}
 */
MyCircularDeque.prototype.isFull = function() {
  // front 和 rear 指向同一个位置,并且此位置上的元素不是 null
  return (this.front === this.rear) && (this.queue[this.front] !== null)
};

3.10 完整代码

git代码地址

/**
 * 题号:641
 * 题目地址:https://leetcode-cn.com/problems/design-circular-deque/
 */


/**
 * @param {number} k
 */
var MyCircularDeque = function(k) {
  // 队列申请到空间的最大容量
  this.max = k
  // 头
  this.front = 0
  // 尾的后一位,  设计的是这种[front, rear)
  this.rear = 0
  // 队列申请到的空间
  this.queue = new Array(k).fill(null)
}

/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularDeque.prototype.insertFront = function(value) {
  // 如果队列满了,插入失败
  if(this.isFull()) return false

  // 否则front指针向前移动一位,但是要考虑front 在 0 的位置时
  this.front = this.front === 0 ? this.max - 1 : this.front - 1

  // 插入
  this.queue[this.front] = value

  // 插入成功
  return true
};

/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularDeque.prototype.insertLast = function(value) {
  // 如果队列满了,插入失败
  if(this.isFull()) return false

  // 插入
  this.queue[this.rear] = value

  // 后移
  // this.rear = this.rear === this.max - 1 ? 0 : this.rear + 1
  this.rear = (this.rear + 1) % this.max

  // 插入成功
  return true
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.deleteFront = function() {
  // 判空,删除失败
  if(this.isEmpty()) return false
  // 删除
  this.queue[this.front] = null
  // 指针后移
  this.front = (this.front + 1) % this.max
  // 删除成功
  return true
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.deleteLast = function() {
  // 判空,删除失败
  if(this.isEmpty()) return false

  // 指针后移
  this.rear = this.rear === 0 ? this.max - 1 : this.rear - 1
  
  // 删除
  this.queue[this.rear] = null

  // 删除成功
  return true

};

/**
 * @return {number}
 */
MyCircularDeque.prototype.getFront = function() {
  // 判空
  if(this.isEmpty()) return -1
  // 返回
  return this.queue[this.front]
};

/**
 * @return {number}
 */
MyCircularDeque.prototype.getRear = function() {
  // 判空
  if(this.isEmpty()) return -1
  // rear 指的是 尾的前一位, 当指向0 时实际指向尾
  return this.queue[this.rear === 0 ? this.max - 1 : this.rear -1]
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.isEmpty = function() {
  // front 和 rear 指向同一个位置,并且此位置上的元素是 null
  return (this.front === this.rear) && (this.queue[this.front] === null)
};

/**
 * @return {boolean}
 */
MyCircularDeque.prototype.isFull = function() {
  // front 和 rear 指向同一个位置,并且此位置上的元素不是 null
  return (this.front === this.rear) && (this.queue[this.front] !== null)
};

/**
 * Your MyCircularDeque object will be instantiated and called as such:
 * var obj = new MyCircularDeque(k)
 * var param_1 = obj.insertFront(value)
 * var param_2 = obj.insertLast(value)
 * var param_3 = obj.deleteFront()
 * var param_4 = obj.deleteLast()
 * var param_5 = obj.getFront()
 * var param_6 = obj.getRear()
 * var param_7 = obj.isEmpty()
 * var param_8 = obj.isFull()
 */