Skip to content

栈(Stack)是一种基于后进先出(Last In, First Out,LIFO)原则的数据结构。在栈中,最后添加的元素最先被访问。栈可以通过数组或链表来实现。

一、栈的操作

1)栈支持两个主要操作:

  • 压栈(Push):
    • 向栈顶添加元素,即将元素放入栈中。
  • 弹栈(Pop):
    • 从栈顶移除元素,即取出栈顶的元素。

2)其他可能的操作:

  • 查看栈顶元素(Peek):
    • 获取栈顶元素的值,但不将其从栈中移除。
  • 判断栈是否为空:
    • 检查栈中是否有元素。

二、栈的应用场景

1)主要应用场景

  • 函数调用: 函数调用时,局部变量和返回地址等信息被压入栈中,函数执行完毕后再弹出。
  • 表达式求值: 计算表达式中的括号匹配问题,以及中缀表达式转换为后缀表达式等。
  • 浏览器历史记录: 在浏览器中,通过前进和后退按钮实现网页浏览历史的管理,可以使用栈结构。
  • 撤销操作: 许多应用程序提供撤销(Undo)功能,可以使用栈来存储历史状态,使用户能够撤销操作。

在实现栈时,可以使用数组或链表。数组实现的栈通常需要提前指定大小,而链表实现的栈可以动态调整大小。

2)具体实例

A、数组形式

在 JavaScript 中,可以使用数组来实现栈。JavaScript 数组天然支持在数组末尾进行元素的添加和删除操作,这正好符合栈的后进先出(LIFO)原则。以下是一个简单的栈的实现示例:

javascript
// 栈的实现类
class Stack {
  constructor() {
    this.items = []
  }

  // 压栈操作
  push(element) {
    this.items.push(element)
  }

  // 弹栈操作
  pop() {
    if (this.isEmpty()) {
      return '栈已空'
    }
    return this.items.pop()
  }

  // 查看栈顶元素
  peek() {
    if (this.isEmpty()) {
      return '栈为空'
    }
    return this.items[this.items.length - 1]
  }

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

  // 获取栈中元素个数
  size() {
    return this.items.length
  }

  // 清空栈
  clear() {
    this.items = []
  }
}

// 示例
const stack = new Stack()

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

console.log(stack.peek()) // 输出 3
console.log(stack.pop()) // 输出 3
console.log(stack.size()) // 输出 2
console.log(stack.isEmpty()) // 输出 false

stack.clear()
console.log(stack.isEmpty()) // 输出 true

上述示例中,Stack 类包含了压栈、弹栈、查看栈顶元素、判断栈是否为空、获取栈中元素个数、清空栈等操作。通过使用数组的 pushpop 方法,可以方便地实现栈的基本功能。

B、链表形式

使用链表实现栈同样是一种常见的方式。在 JavaScript 中,可以通过定义一个链表节点(Node)和一个栈类(Stack)来实现。以下是一个简单的链表实现的栈的例子:

javascript
// 定义链表节点
class Node {
  constructor(data) {
    this.data = data
    this.next = null
  }
}

// 栈的实现类
class Stack {
  constructor() {
    this.top = null // 栈顶节点
    this.size = 0 // 栈中元素个数
  }

  // 压栈操作
  push(element) {
    const newNode = new Node(element)
    newNode.next = this.top
    this.top = newNode
    this.size++
  }

  // 弹栈操作
  pop() {
    if (this.isEmpty()) {
      return '栈已空'
    }

    const removedNode = this.top
    this.top = removedNode.next
    removedNode.next = null
    this.size--

    return removedNode.data
  }

  // 查看栈顶元素
  peek() {
    if (this.isEmpty()) {
      return '栈为空'
    }
    return this.top.data
  }

  // 判断栈是否为空
  isEmpty() {
    return this.size === 0
  }

  // 获取栈中元素个数
  getSize() {
    return this.size
  }

  // 清空栈
  clear() {
    this.top = null
    this.size = 0
  }
}

// 示例
const stack = new Stack()

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

console.log(stack.peek()) // 输出 3
console.log(stack.pop()) // 输出 3
console.log(stack.getSize()) // 输出 2
console.log(stack.isEmpty()) // 输出 false

stack.clear()
console.log(stack.isEmpty()) // 输出 true

在上述示例中,Node 类表示链表的节点,每个节点包含一个数据域 data 和一个指向下一个节点的指针 nextStack 类表示栈,使用链表来实现栈的基本操作。压栈操作将新节点添加到链表的头部,而弹栈操作将链表头部的节点移除。这样的实现方式允许栈的大小可以动态变化,而不受固定数组大小的限制。