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

摘要:
首先,写出Stack的构造函数functionStack(){this.__items=[]}3.2.1push实现分析:向数组尾部添加元素,同JS数组操作functionStack(){this.__items=[]Stack.prototype.push=function{returnthis.__items.push}}3.2.2pop实现分析:删除/弹出数组最后一个元素,同JS数组操作functionStack(){this.__items=[]Stack.prototype.pop=function(){returnthis.__items.pop()}}3.2.3peek实现分析:查看栈顶即数组最后一个元素functionStack(){this.__items=[]Stack.prototype.peek=function(){if(!this.__items.length)returnnullreturnthis.__items[this.__items.length-1]}}3.2.4isEmpty实现分析:只要看内部数组元素个数是否为0functionStack(){this.__items=[]Stack.prototype.isEmpty=function(){returnthis.__items.length===0}}3.2.5size实现分析:返回内部数组元素个数functionStack(){this.__items=[]Stack.prototype.size=function(){returnthis.__items.length}}3.2.6clear实现分析:清空内部数组functionStack(){this.__items=[]Stack.prototype.clear=function(){this.__items.length=0}}3.2.7toString实现分析:将内部数组用join连结functionStack(){this.__items=[]Stack.prototype.toString=function(){returnthis.__items.join('')}}完整代码如下://栈functionStack(){this.__items=[]//入栈Stack.prototype.push=function{returnthis.__items.push}//出栈Stack.prototype.pop=function(){returnthis.__items.pop()}//查看栈顶Stack.prototype.peek=function(){if(!
【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进制值是多少

原十进制值取模等于余数
8240
4220
2210
1201

这样,得到的二进制就是: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-

免责声明:文章转载自《【JavaScript数据结构系列】02-栈Stack》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇下载Tomcat时Tomcat网站上的core和deployer的区别使用OPENROWSET函数连接并访问远程数据库数据下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

OptimalSolution(5)--数组和矩阵问题(1)简单

  一、转圈打印矩阵   题目:给定一个整型矩阵matrix,按照转圈的方式打印它。   要求:额外空间复杂度为O(1) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 打印结果为:1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10   思路:矩阵分圈处理问题。用...

Android通过流播放声音

Android通过流播放声音 【转载】http://mobile.51cto.com/amedia-375030.htm AudioRecord和AudioTrack类是Android获取和播放音频流的重要类,放置在android.media包中。与该包中的 MediaRecorder和MediaPlayer类不同,AudioRecord和AudioTra...

java8使用parallelStream并行流造成数据丢失或下标越界异常解决方案

描述 我们先看一段使用了并行流的代码 @Test public void testStream() { List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { li...

Java常用类库与技巧

Java异常 异常处理机制主要回答了三个问题 What:异常类型回答了什么被抛出 Where:异常堆栈跟踪回答了在哪抛出 Why:异常信息回答了为什么被抛出 Java的异常体系 ​ Error和Exception的区别 从概念角度解析Java的异常处理机制: 1.Error:程序无法处理的系统处理,编辑器不做检查(如系统崩溃,虚拟机错误,内存空间不足,方法...

confirmit平台问题汇总

Html Styles下任意一个复制样式系统命名的问题:  当某个题中某个元素需要单独设置CSS样式时,复制一份全局样式后,引用复制的那个样式scale (2)会失效。原因:系统默认生成的这个class名称其实是两个class名称( )  , 所以我们引用这个样式会失效。解决方案:自己手动改个合适的单独的class名称。 手机端不能直接给input设置...

可持久化数组入门

# 洛谷题目链接:[可持久化数组](https://www.luogu.org/problemnew/show/P3919) 题目描述 如题,你需要维护这样的一个长度为 NN 的数组,支持如下几种操作 在某个历史版本上修改某一个位置上的值 访问某个历史版本上的某一位置的值 此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),...