栈也是一种 非常常见 的数据结构,并且在程序中的 应用非常广泛
数组
- 我们知道数组是一种线性结构,并且可以在数组的 任意位置 插入和删除数据
- 但是有时候,我们为了实现某些功能,必须对这种任意性 加以 限制
- 而 栈和队列 就是比较常见的 受限的线性结构,我们先来学习栈结构
- 栈结构示意图
- 先入后出,后入先进
栈( stack ),它是一种受限的线性表,后进先出(LIFO)
- 其限制是仅允许在 表的一端 进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。
- LIFO(last in first out)表示就是后进入的元素,第一个弹出栈空间.类似于自动餐托盘,最后放上的托盘,往往先 把拿出去使用.
- 向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素
- 从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
生活中的例子
- 自助餐的托盘,最新放上去的,最先被客人拿走使用
- 收到很多的邮件(实体的),从上往下依次处理这些邮件.(最新到的邮件,最先处理
- 注意:不允许改变邮件的次序,比如从最小开始,或者处于最紧急的邮件,否则就不再是栈结构了。而是队列或者 优先级队列结构.
程序中什么是使用栈实现的呢?
- 学了这么久的编程,是否听说过,函数调用栈呢?
- 我们知道函数之间和相互调用: A调用B,B中又调用,C中又调用D
- 那样在执行的过程中,会先将A压入栈,A没有执行完,所有不会弹出栈
- 在A执行的过程中调用了B,会将B压入到栈,这个时候B在栈顶,A在栈底.
- 如果这个时候B可以执行完,那么B会弹出栈.但是B有执行完吗? 没有它调用了C.
- 所以C会压栈,并且在栈顶.而C调用了D,D会压入到栈顶.
- 所以当前的栈顺序是: 栈顶A->B->C->D顶
- D执行完, 弹出栈.C/B/A依次弹出栈
- 所以我们有函数调用栈的称呼,就来自于它们内部的实现机制.(通过栈来实现的)
练习题
问:有六个元素6,5,4,3,2,1 的顺序进栈,问下列哪一个不是合法的出栈序列?( )
- A.543612
- B.453216
- C.346521
- D.234156
- 注意:顺序入栈,5进下一个就是4,4进下一个就是3,3进下一个就是2
栈的常见操作
- push:添加一个新的元素到栈顶
- pop:移除一个元素同时返回移除元素组
- peek:查看栈顶元素
- isEmpty:如果栈没有任何元素就返回true反之false
- size:返回栈里的元素个数
- toString:将栈结构的内容以字符形式返回
<script>
//封装栈
function Stack() {
this.arr = []
//栈的添加
Stack.prototype.push = function (ele) {
this.arr.push(ele)
}
//栈的删除pop
Stack.prototype.pop = function () {
return this.arr.pop() //返会的就是最后一个元素
}
//栈的查询
Stack.prototype.peek = function () {
return this.arr[this.arr.length - 1]
}
//栈的判空
Stack.prototype.isFlag = function () {
return this.arr.length == 0
}
//获取栈的元素的个数
Stack.prototype.size = function () {
return this.arr.length
}
//toString()方法
Stack.prototype.toString = function () {
var objstring = ''
for (let i = 0; i < this.arr.length; i++) {
objstring += this.arr[i] + ' '
}
return objstring
}
}
let a = new Stack()
a.push(20)
a.push(30)
a.push(60)
alert(a) //添加 20 30 60
a.pop() //删除最后一个进入栈的元素
alert(a) // 20 30
alert(a.peek()) //返回栈的长度 2
alert(a.isFlag()) //返回true或false 栈里有元素返回false,栈里没有元素返回true 这里返回false
alert(a.size()) //返回栈的长度 2
console.log(a.toString()); //返回 20 30 60
</script>