zoukankan      html  css  js  c++  java
  • 【JavaScript数据结构系列】02-栈Stack

    【JavaScript数据结构系列】02-栈Stack

    码路工人 CoderMonkey

    转载请注明作者与出处


    ## 1. 认识栈结构

    栈是非常常用的一种数据结构,
    与数组同属线性数据结构,
    不同于数组的是它是一种受限的线性结构。

    画一张图来说明:

    stack


    如上图所示,

    • 最新入栈的元素/最底下的元素,为栈底
    • 最后一个/最上面的元素,为栈顶
    • 最后一个入栈元素最先出栈(LIFO原则)
    • 只能操作栈顶
    • 添加元素叫:进栈/压栈/入栈
    • 删除元素叫:出栈/退栈

    ## 2. 栈的应用:
    • 函数调用栈
    • 文本编辑器中的撤销与重做

    等。

    3. 栈的实现

    注:
    ES6 版的代码实现请查看 npm 包 data-struct-js 代码
    代码在 Github/Gitee

    3.1 常用方法

    方法 描述
    push(element) 添加元素到栈顶
    pop() 删除栈顶元素
    peek()
    查看栈顶元素
    isEmpty() 检查是否为空栈
    size() 检查栈容量
    clear() 清空栈
    toString() 字符串化

    3.2 常用方法的代码实现

    栈的实现可以基于数组,也可以基于链表,
    这里我们用基于数组来实现一下。

    首先,写出Stack的构造函数

    function Stack() {
      this.__items = []  
    }
    

    3.2.1 push

    实现分析:
    向数组尾部添加元素,同JS数组操作

    function Stack() {
    	this.__items = []
      
      Stack.prototype.push = function(element) {
      	return this.__items.push(element)
      }
    }
    

    3.2.2 pop

    实现分析:
    删除/弹出数组最后一个元素,同JS数组操作

    function Stack() {
    	this.__items = []
      
      Stack.prototype.pop = function() {
      	return this.__items.pop()
      }
    }
    

    3.2.3 peek

    实现分析:
    查看栈顶即数组最后一个元素

    function Stack() {
    	this.__items = []
      
      Stack.prototype.peek = function() {
      	if(!this.__items.length) return null
        return this.__items[this.__items.length-1]
      }
    }
    

    3.2.4 isEmpty

    实现分析:
    只要看内部数组元素个数是否为0

    function Stack() {
    	this.__items = []
      
      Stack.prototype.isEmpty = function() {
        return this.__items.length === 0
      }
    }
    

    3.2.5 size

    实现分析:
    返回内部数组元素个数

    function Stack() {
    	this.__items = []
      
      Stack.prototype.size = function() {
        return this.__items.length
      }
    }
    

    3.2.6 clear

    实现分析:
    清空内部数组

    function Stack() {
    	this.__items = []
      
      Stack.prototype.clear = function() {
        this.__items.length = 0
      }
    }
    

    3.2.7 toString

    实现分析:
    将内部数组用 join 连结

    function Stack() {
    	this.__items = []
      
      Stack.prototype.toString = function() {
        return this.__items.join(' ')
      }
    }
    

    完整代码如下:

    // 栈
    function Stack() {
        this.__items = []
        // 入栈
        Stack.prototype.push = function (element) {
            return this.__items.push(element)
        }
        // 出栈
        Stack.prototype.pop = function () {
            return this.__items.pop()
        }
        // 查看栈顶
        Stack.prototype.peek = function () {
            if (!this.__items.length) return null
            return this.__items[this.__items.length - 1]
        }
        // 是否为空栈
        Stack.prototype.isEmpty = function () {
            return this.__items.length === 0
        }
        // 获取栈大小/容量/元素个数​
        Stack.prototype.size = function () {
            return this.__items.length
        }
        // 清空栈
        Stack.prototype.clear = function() {
          this.__items.length = 0
        }
        // 字符串值
        Stack.prototype.toString = function () {
            return this.__items.join(' ')
        }
    }
    
    

    3.3 下面我们测试一下:

    // ---------------------------------------------
    // Test
    // ---------------------------------------------
    var s = new Stack()
    for(var i = 0; i < 5; i++){
        s.push(i)
    }
    console.log('isEmpty: ',s.isEmpty())	// isEmpty: false
    console.log('size: ',s.size())				// size: 5
    console.log(s.toString())							// 0 1 2 3 4
    while(s.size()) {
        console.log(`pop: `, s.pop())			// 4 3 2 1 0
    }
    console.log('isEmpty: ',s.isEmpty())	// isEmpty: true
    console.log('size: ',s.size())				// size: 0
    

    我们得到了符合预期的结果。

    在上面的实现中,没有考虑复杂类型时的引用传递问题,
    也没有遍历方法,这些我们将在后面补充完善。

    4. 思考题

    4.1 判断哪个不是可能的出栈顺序?()

    有六个元素`6,5,4,3,2,1`依次进栈,下列哪一个不是合法的出栈顺序?
    - A. 5 4 3 6 1 2
    - B. 4 5 3 2 1 6
    - C. 3 4 6 5 2 1
    - D. 2 3 4 1 5 6
    

    这个选择题还是非常简单的,
    满足先进后出画图一试答案就出来了。


    下面利用栈结构实现十进制转二进制。
    比起网上的其它例子,
    这里稍微复杂了的一点在于,
    不仅仅针对正整数,考虑了负数。

    4.2 十进制整数转二进制的方法实现

    4.2.1 转换的数学方法

    将一个十进制的数转为二进制,

    • 将这个数与2取模,记录余数,将商用于下一次计算
    • 重复上一步,直到商为0
    • 将得到的余数由后向前连接起来,即为所求二进制结果

    举例:计算8的2进制值是多少

    原十进制值 取模 等于 余数
    8 2 4 0
    4 2 2 0
    2 2 1 0
    1 2 0 1

    这样,得到的二进制就是:1000
    (也即 0000 1000)

    负数时的规则:反码后加1补码
    所以 -8 的二进制计算过程为:

    • 0000 1000取反:1111 0111
    • 补码:1111 1000

    4.2.2 转换的代码实现

    基于栈的实现,这里就不重复贴 Stack.js 的代码了。

    代码实现分析:

    • 等于0的情况,直接返回0
    • 大于0的情况,按照我们上面列出的转换方法
      • 与2取模,保存入栈,直到商为0
      • 依序出栈,拼接结果并返回
    • 小于0的情况,比起以上两种情况来说最复杂
      • 先乘以-1转为正整数,再按照大于0的情况进行取模处理
      • 取模结果要取反(即0变1,1变0),然后保存入栈
      • 计算完,依序出栈,得到中间结果
      • 将上面得到的结果值 +1 进行补码
      • 再在首位前加上一位1来表示负数
      • TODO:其实还需要完善一点,空位用1补足8位(或其它可能的位数)
    // ---------------------------------------------
    // decimal to binary
    // ---------------------------------------------
    function dec2bin(decNum) {
        if(!decNum) return 0
    
        var stack = new Stack()
        var minus = decNum < 0
        
        if(minus) {
            decNum *= -1
            while(decNum) {
                var temp = decNum % 2
                switch(temp) {
                    case 0:
                        temp = 1
                        break
                    case 1:
                        temp = 0
                        break
                }
                stack.push(temp)
                decNum = parseInt(decNum / 2)
            }
        } else {
            while(decNum) {
                stack.push(decNum % 2)
                decNum = parseInt(decNum / 2)
            }
        }
    
        var result = ''
        while(!stack.isEmpty()) {
            result += stack.pop()
        }
    
        if(minus) {
            // 补码
            for(var i=result.length-1;i>=0;i--) {
                var arrTemp = result.split('')
                switch(result[i]) {
                    case '0':
                        arrTemp[i] = 1
                        break
                    case '1':
                        arrTemp[i] = 0
                        break
                }
                result = arrTemp.join('')
            }
            result = '1' + result
        }
    
        return result
    }
    
    var ret = dec2bin(8)
    console.log(' 8 => ', ret)    // 1000
    
    ret = dec2bin(-8)
    console.log('-8 => ', ret)    // 1 1000
    

    以上就是完整代码,运行一下试试吧。


    基于 ES6 实现的 JavaScript 数据结构,
    虽然这个小轮子很少会被使用,
    也许对于初学者学习 JavaScript 会有点帮助。
    只要简单 install 一下即可,感兴趣的话还可以去
    GitHub / Gitee 看源码。(Star 表支持~)

    npm install data-struct-js --save-dev
    

    https://github.com/CoderMonkie/data-struct-js
    https://gitee.com/coder-monkey/data-struct-js

    最后,感谢您的阅读和支持~


    -end-
  • 相关阅读:
    gain 基尼系数
    luogu P5826 【模板】子序列自动机 主席树 vector 二分
    牛客挑战赛39 树与异或 离线 树上莫队 树状数组 约数
    4.22 省选模拟赛 三元组 manacher 回文自动机
    4.22 省选模拟赛 最优价值 网络流 最大权闭合子图
    4.18 省选模拟赛 消息传递 树剖 倍增 线段树维护等比数列
    luogu P4008 [NOI2003]文本编辑器 splay 块状链表
    牛客挑战赛39 密码系统 后缀数组
    luogu P1526 [NOI2003]智破连环阵 搜索+最大匹配+剪枝
    luogu P4095 [HEOI2013]Eden 的新背包问题 多重背包 背包的合并
  • 原文地址:https://www.cnblogs.com/CoderMonkie/p/js-data-struct-stack.html
Copyright © 2011-2022 走看看