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-
  • 相关阅读:
    粒子系统(二):绘制精美几何图案
    图像识别:微信跳一跳机器人
    粒子系统(一):从零开始画一颗树
    Unity3D对弈游戏:狼吃羊游戏
    编程模拟自然(九):元胞自动机
    自动绘图AI:程序如何画出动漫美少女
    UWP简单示例(三):快速开发2D游戏引擎
    jsoup开发网页客户端3
    Android 自定义Dialog类,并在Activity中实现按钮监听。
    Jsoup开发网站客户端第二篇,图片轮播,ScrollView兼容ListView
  • 原文地址:https://www.cnblogs.com/CoderMonkie/p/js-data-struct-stack.html
Copyright © 2011-2022 走看看