第3章 栈 (Stack)

作者: 晓风残月1994 | 来源:发表于2020-08-06 09:28 被阅读0次

    栈和队列类似于数组,但在添加和删除元素时更为可控。

    1. 栈数据结构

    栈是一种遵从后进先出(LIFO)原则的有序集合,两端分别是栈顶和栈底。新添加的或者待删除的元素都保存在栈顶。编译器和内存中保存变量、方法调用等都会用到栈。


    image.pngimage.png

    1.1 创建栈

    • push(element(s)):添加一个(或几个)新元素到栈顶。
    • pop():移除栈顶的元素,同时返回被移除的元素。
    • peek():返回栈顶的元素,不对栈做任何修改(这个方法不会移除栈顶的元素,仅仅返回它)。
    • isEmpty():如果栈里没有任何元素就返回true,否则返回false。
    • clear():移除栈里的所有元素。
    • size():返回栈里的元素个数。这个方法和数组的length属性很类似。

    1.1.1 传统方式

    function Stack() {
      let items = [];
    
      this.push = function (element) {
        items.push(element);
      };
    
      this.pop = function () {
        return items.pop();
      };
    
      this.peek = function () {
        return items[items.length - 1];
      };
    
      this.isEmpty = function () {
        return items.length === 0;
      };
    
      this.size = function () {
        return items.length;
      };
    
      this.clear = function () {
        items = [];
      };
    
      this.print = function () {
        console.log(items.toString());
      };
    }
    

    1.1.2 ES6 Class

    使用了 WeakMap,键是对象,值是任意数据类型。WeakMap 对比 Map 而言(WeakSet 同理):

    • 没有 entries、keys、values 等迭代器方法;
    • 只能用对象作为键。

    这里键是 this,值是数组。从而每个实例都可以根据自己的 this 获取私有的数组池。除非知道别的实例 this,否则也不会取出别的实例中的值(因为没有迭代器方法)。

    const Stack = (function () {
      const items = new WeakMap();
    
      class Stack {
        constructor() {
          items.set(this, []);
        }
    
        push(element) {
          const s = items.get(this);
          s.push(element);
        }
    
        pop() {
          const s = items.get(this);
          return s.pop();
        }
    
        peek() {
          const s = items.get(this);
          return s[s.length - 1];
        }
    
        isEmpty() {
          const s = items.get(this);
          return s.length === 0;
        }
    
        size() {
          const s = items.get(this);
          return s.length;
        }
    
        clear() {
          items.set(this, []);
        }
    
        print() {
          const s = items.get(this);
          console.log(s.toString());
        }
      }
    
      return Stack;
    })();
    

    2. 栈相关问题

    据说栈有三个著名的算法场景:

    • 十进制转二进制
    • 平衡圆括号
    • 汉诺塔问题

    2.1 十进制转二进制

    image.pngimage.png
    /**
     * 十进制数字和2整除,直至结果是0,然后反向输出余数,得到转换后的二进制
     */
    function convert10To2(decNumber) {
      let remStack = new Stack(),
        rem,
        binaryString = '';
    
      while (decNumber > 0) {
        rem = decNumber % 2;
        remStack.push(rem);
        decNumber = Math.floor(decNumber / 2);
      }
    
      while (!remStack.isEmpty()) {
        binaryString += remStack.pop().toString();
      }
    
      return binaryString;
    }
    
    const test = 15;
    const result = convert10To2(test);
    console.log(result);
    console.log('测试转换结果是否正确:', result === test.toString(2));
    

    2.2 任意进制转十进制

    /**
     * 任意进制转十进制(十六进制及以内)
     */
    function baseConverter(decNumber, base) {
      let remStack = new Stack(),
        rem,
        baseString = '',
        digits = '0123456789ABCDEF';
    
      while (decNumber > 0) {
        rem = decNumber % base;
        remStack.push(rem);
        decNumber = Math.floor(decNumber / base);
      }
    
      while (!remStack.isEmpty()) {
        baseString += digits[remStack.pop()];
      }
    
      return baseString;
    }
    
    const test2 = 2501234;
    const result2 = baseConverter(test2, 16);
    console.log(result2);
    console.log('测试转换结果是否正确:', result2 === test2.toString(16).toUpperCase());
    

    2.3 判断括号是否平衡

    // https://leetcode.com/problems/valid-parentheses/
    
    /**
     * 判断括号是否匹配
     *
     * @param {string} s
     * @return {boolean}
     */
    var isValid = function (s) {
      if (s.length === 0) return true;
    
      const stack = [];
      // 遇见左括号就入栈,右括号就弹出栈顶进行匹配
      // 平衡多少对儿,就弹出多少个左括号
      // 若能全部平衡,则最后栈应为空
      const dict = {
        ')': '(',
        '}': '{',
        ']': '['
      };
    
      const isLeft = (c) => '({['.includes(c);
    
      if (!isLeft(s[0])) return false;
    
      for (let i = 0; i < s.length; i++) {
        const c = s[i];
        if (isLeft(c)) {
          stack.push(c);
        } else {
          if (dict[c] !== stack.pop()) {
            return false;
          }
        }
      }
    
      return stack.length === 0;
    };
    

    相关文章

      网友评论

        本文标题:第3章 栈 (Stack)

        本文链接:https://www.haomeiwen.com/subject/etzorktx.html