数据结构与算法 (二)栈

242 阅读4分钟

什么是栈?

我觉得理解数据结构的时候在脑海中必须要有一种模型,比如当提到的时候,我们就要想起手枪弹夹,联想一下子弹是不是先进后出,后进先出的。

image.png 还有生活中的放托盘最后放上的托盘,最先放置的使用的时候往往最后被取出使用,有了以上的这种概念之后我们在进行下一步的总结。

栈(stack)是一种受限的线性表:

  • LIFO( last in first out)表示后进入的元素,第一个弹出栈空间。
  • 限制只允许在线性表的一端进行插入或删除操作。这一端被称为栈顶,相对的,把另一端称为栈底。
  • 向一个栈插入新元素又称为进栈入栈、或压栈,这个操作是把新元素放到栈顶元素的上面,使之称为新的栈顶元素。
  • 从一个栈中删除元素又称为出栈退栈,它是把栈顶元素删除掉,使其相邻元素称为新的栈顶元素。

下面有图的形式来表示一下的结构

image.png

栈的特点:先进后出,后进先出

程序中的栈结构

  • 函数调用栈:A(B(C(D()))): 即 A 函数中调用 B,B 调用 C,C 调用 D;在 A 执行的过程中会将 A 压入栈,随后 B 执行时 B 也被压入栈,函数 C 和 D 执行时也会被压入栈。所以当前栈的顺序为:A->B->C->D(栈顶);函数 D 执行完之后,会弹出栈被释放,弹出栈的顺序为 D->C->B->A;
  • 递归: 为什么没有停止条件的递归会造成栈溢出?比如函数 A 为递归函数,不断地调用自己(因为函数还没有执行完,不会把函数弹出栈),不停地把相同的函数 A 压入栈,最后造成栈溢出(Queue Overfloat)。

练习

题目:有 6 个元素 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

题目解释。题目所说的按顺序进栈指的不是一次性全部进栈,而是有进有出,进栈顺序为 6 -> 5 -> 4 -> 3 -> 2 -> 1。

A

5         4         3         1
6 -> 6 -> 6 -> 6 -> 6 -> 6 -> 2 -> 2

B

4
5    5         3         2    1
6 -> 6 -> 6 -> 6 -> 6 -> 6 -> 6 -> 6

C

3
4    4   
5    5    5  ? 5还没出栈6怎么能出栈呢????
6 -> 6 -> 6

D

2
3    3
4    4    4         1
5    5    5    5    5    5
6 -> 6 -> 6 -> 6 -> 6 -> 6 -> 6

栈结构实现

栈常见的操作

  • push() 添加一个新元素到栈顶位置。
  • pop() 移除栈顶的元素,同时返回被移除的元素。
  • peek() 返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)。
  • isEmpty() 如果栈里没有任何元素就返回 true,否则返回 false
  • size() 返回栈里的元素个数。这个方法和数组的 length 属性类似。
  • print() 将栈结构的内容以字符串的形式返回。

JavaScript 代码实现栈结构

//采用Es5  的写法来写。
//栈可以抽象成给手枪弹夹装子弹。
function Stack() {
  //用来存储数据
  this.items = []
}

//定义栈的基本操作

//1. 把元素压入栈中
Stack.prototype.push = function (element) {
  this.items.push(element)
}
//2. 从栈中取出元素
Stack.prototype.pop = function () {
  return this.items.pop()
}

//3. 查看一下栈顶元素
Stack.prototype.peek = function () {
  return this.items[this.items.length - 1]
}

//4. 判断栈是否为空
Stack.prototype.isEmpty = function () {
  return this.items.length === 0
}

//5. 获取栈中的元素个数
Stack.prototype.size = function () {
  return this.items.length
}

//6. 查看当前栈里面的内容

Stack.prototype.print = function () {
  return this.items.join(' ')
}

module.exports = Stack

测试封装的栈结构

const Stack = require('./stack')

var stack = new Stack()

stack.push(1)
stack.push(2)
stack.push(3)
stack.push(4)
stack.push(5)

console.log(stack)               //Stack { items: [ 1, 2, 3, 4, 5 ] }

stack.pop()

console.log(stack)              //Stack { items: [ 1, 2, 3, 4 ] }

console.log(stack.peek())       //4

console.log(stack.isEmpty())    //false

console.log(stack.size())       //4

console.log(stack.print())      //1 2 3 4

栈结构的简单应用

利用栈结构的特点封装实现十进制转换为二进制的方法。

正整数转成二进制。要点一定一定要记住哈:除二取余,然后倒序排列,高位补零。

代码实现

function dec2bin(dec) {
  let stack = new Stack()

  while (dec > 0) {
    //进栈
    stack.push(dec % 2)
    dec = Math.floor(dec / 2)
  }
  let bin = ""
  while (!stack.isEmpty()) {
    bin += stack.pop();
  }
  return bin
}

let bin = dec2bin(10)

console.log(bin) //1010