【JavaScript-Map】JavaScript用Map类型对象实现栈结构

JavaScript用Map类型对象实现栈结构

前言

在做【146. LRU 缓存机制 - 力扣(LeetCode)】这道题时,我一开始以为不能用Map来实现栈结构,但是一番思考加实践后,我发现好像是可以的。与这道题相关的解释可以可以参看下方博客:

更新:2021年9月12日24:07:55

参考:【算法-LeetCode】146. LRU 缓存机制(双向链表;Map;栈)_赖念安的博客-CSDN博客

一般来说,我都会选择数组来实现栈结构,因为JavaScript的数组对象提供了 pop()push() 这两个非常方便的原生函数来让我们操作数组末尾的元素。而栈先进后出的特性则正好是要求我们对栈顶的元素进行操作,所以很容易就会想到这两者之间的实现关系。

下面我就尝试使用JavaScript中的Map对象来实现【栈】这种数据结构。

栈的特点及功能函数

首先我们要明白【栈】最大的特点就是先进后出(First In Last Out)。也就是说我们操作的都是栈顶的元素。

一个简单顺序栈结构大致应该有以下几个功能(或者说是函数):

init():初始化一个栈,获得一个空栈。
clear():清空一个栈里的元素。
isEmpty():判断一个栈是否为空,获得一个类型为布尔值的判断结果。
isFull():判断一个栈是否已满,获得一个类型为布尔值的判断结果。
push():在栈的顶部压入一个新的元素,如果原栈未满,则在其顶部压入目标元素,
 并返回true代表压入成功;如果原栈已满,则直接返回 false 表示压入失败。
pop():在栈不为空的前提下,弹出栈顶元素,并将该元素作为返回值返回。如果操作  失败则返回 false
getTop():获取栈顶元素并将其作为返回值返回。如果是空栈则返回 null

JS代码实现

以下是具体的实现JavaScript代码:

// Stack.js

/*
 * @Descripttion: 用JavaScript中的Map对象实现栈结构
 * @version: 0.1
 * @Author: LiarCoder
 * @Date: 2021-09-13 16:48:36
 * @LastEditors: LiarCoder
 * @LastEditTime: 2021-09-13 20:04:12
 */

class Stack {
  /**
   * @description: Stack类的构造函数
   * @param {number} capacity 创建Stack类实例时,可以传入一个正整数来限定栈的最大容量
   * @return {Stack}
   */
  constructor(capacity) {
    /**
     * 【更新:2021年9月13日19: 46: 22】
     * 增加了对 capacity 有效性的判断,传入的 capacity 应该是一个大于 0 
     * 且小于等于 Number.MAX_SAFE_INTEGER(9007199254740991)的整数,
     * 如果不传入的话,则栈的默认最大容量就为 Number.MAX_SAFE_INTEGER
     * 【更新:2021年9月14日09:36:50】
     * 思考一番,还是觉得 capacity 不接受设置为 0 比较好,因为那样好像没有什么意义
     */

    if ((Number.isSafeInteger(capacity) && capacity > 0) || capacity === undefined) {
      this.capacity = capacity === undefined ? Number.MAX_SAFE_INTEGER : Number(capacity);
      this.top = null;
      this.size = 0;
      this.container = new Map();
    } else {
      throw new Error('The capacity of the stack should be an integer greater than 0 and less than or equal to Number.MAX_SAFE_INTEGER');
    }
  }

  /**
   * @description: Stack 类身上的静态方法,不能通过 Stack 实例对象调用,用于初始化一个空栈
   * @param {*} 默认不需要参数
   * @return {Stack} 返回一个空栈对象 
   */
  static init() {
    return new Stack();
  }

  /**
   * @description: Stack 类的实例方法,可以通过 Stack 实例对象调用,用于清空一个栈
   * @param {*} 默认不需要参数
   * @return {*} 没有特定返回值,默认返回 undefined
   */
  clear() {
    this.container.clear();
    this.top = null;
    this.size = 0;
  }

  /**
   * @description: Stack 类的实例方法,可以通过 Stack 实例对象调用,用于判断一个栈是否为空
   * @param {*} 默认不需要参数
   * @return {Boolean} 若目标栈为空,则返回 true,否则返会 false
   */
  isEmpty() {
    return this.size ? false : true;
  }

  /**
   * @description: Stack 类的实例方法,可以通过 Stack 实例对象调用,用于判断一个栈是否已满
   * @param {*} 默认不需要参数
   * @return {Boolean} 若目标栈已满,则返回 true,否则返会 false
   */
  isFull() {
    return this.size === this.capacity ? true : false;
  }

  /**
   * @description: Stack 类的实例方法,可以通过 Stack 实例对象调用,用于向栈顶压入一个新的元素
   * @param {*} value 可以传入任意类型的值
   * @return {Boolean} 如果目标栈已满,则直接返回 false 表示操作失败;否则返回 true 表示压入成功
   */
  push(value) {
    if (this.isFull()) {
      return false;
    }
    this.container.set(this.size + 1, value);
    this.top = value;
    this.size++;
    return true;
  }

  /**
   * @description: Stack 类的实例方法,可以通过 Stack 实例对象调用,用于弹出栈顶元素
   * @param {*} 默认不需要参数
   * @return {Boolean} 如果目标栈已满,则直接返回 false 表示操作失败;否则返回栈顶元素
   */
  pop() {
    if (this.isEmpty()) {
      return false;
    }

    // 本来是想利用Map对象遍历顺序和插入顺序一致的这个特性来操作栈顶的,
    // 但是发现没那个必要,因为我给Stack定义了一个size属性来记录栈目前的尺寸(元素个数)
    // 而这个size属性又刚好作为Map的key值,所以可以直接通过size来操作栈顶
    // let keyArr = [...this.container.keys()];
    // let topElement = this.container.get(keyArr[keyArr.length - 1]);
    // this.container.delete(keyArr[keyArr.length - 1]);

    let topElement = this.container.get(this.size);
    this.container.delete(this.size);
    this.size--;
    this.top = this.container.get(this.size);
    return topElement;
  }

  /**
   * @description: Stack 类的实例方法,可以通过 Stack 实例对象调用,用于获取栈顶元素
   * @param {*} 默认不需要参数
   * @return {*} 栈顶元素
   */
  getTop() {
    return this.top;
  }
}

let testStack = new Stack();
// let testStack = Stack.init();
console.log(testStack);
console.log(testStack.pop());
console.log(testStack.push('one'));
console.log(testStack.push('two'));
console.log(testStack.push('three'));
console.log(testStack);
console.log(testStack.pop());
console.log(testStack);
console.log(testStack.isEmpty());
console.log(testStack.isFull());
console.log(testStack.clear());
console.log(testStack);
console.log(testStack.isEmpty());
console.log(testStack.isFull());

我本来是想利用【Map 的遍历顺序就是插入顺序】这一特性来实现栈结构的,但是在实现的过程中发现好像没那个必要,只要利用 size 这个属性是随着栈长度改变而改变的就可以借之完成对栈顶元素的操作。

有关参考

更新:2021年9月13日20:07:16
参考:Class 的基本语法 - ES6 教程 - 网道
参考:Set 和 Map 数据结构 - ES6 教程 - 网道
参考:Number 对象 - JavaScript 教程 - 网道
参考:Number.isSafeInteger() - JavaScript | MDN
参考:【算法-LeetCode】146. LRU 缓存机制(双向链表;Map;栈)_赖念安的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值