尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

栈的全面解析:ADT、实现与应用 - 指南

栈的全面解析:ADT、实现与应用 - 指南
📅 发布时间:2026/6/22 4:18:01

栈的全面解析:ADT、实现与应用

一、栈的基础概念与ADT

栈是一种先进后出(FILO, First In Last Out)的线性数据结构,它就像现实生活中堆叠的盘子,只能从顶部添加或移除元素。在计算机科学中,栈的"栈顶"是唯一可操作的位置,而"栈底"固定不动。

作为抽象数据类型(ADT),栈提供了一组标准操作:

  • push:向栈顶添加元素
  • pop:移除并返回栈顶元素
  • peek:查看栈顶元素但不移除
  • isEmpty:判断栈是否为空
  • size:获取栈中元素数量

这些操作确保了栈的"后进先出"特性,使其在程序执行、表达式求值、括号匹配等场景中发挥着关键作用。

二、数组实现栈

数组是开箱即用的线性数据结构。在JavaScript中数组的相关操作:

// 在JavaScript数组的相关操作
const arr = [1,2,3];
arr.push(4); // 尾部添加
arr.unshift(0); // 头部添加
console.log(arr); // [0,1,2,3,4]
arr.pop(); // 弹出尾部元素
arr.shift(); // 弹出头部元素
console.log(arr); // [1,2,3]

在用数组实现栈之前我们先了解以下知识:

ES6引入的class语法为实现栈提供了优雅的解决方案。通过class,我们可以创建一个类模板,封装栈的所有属性和方法。

es6引入的constructor(构造方法)—— 它是 ES6 class 中唯一用于初始化实例的特殊方法,当通过 new 关键字创建类的实例时,constructor 会自动执行,核心作用是初始化实例的属性(尤其是私有属性,如下面代码中的#stack)。

es6 中,get 和 set 是用于定义访问器属性(Accessor Property) 的语法,访问器属性不直接存储值,而是通过函数拦截对属性的读取(get) 和赋值(set) 操作,隐藏内部状态(封装性),从而实现对属性访问的精细化控制。

数组实现栈代码

 // 数组来实现stack
class ArrayStack {#stack;constructor() {this.#stack = [];}get size() {return this.#stack.length;}isEmpty() {return this.size === 0;}push(num) {this.#stack.push(num);}pop() {if(this.isEmpty()) throw new Error("栈为空");return this.#stack.pop();}peek() {if(this.isEmpty()) throw new Error("栈为空");return this.#stack[this.size -1];}toArray() {return this.#stack;}
}

代码相关解释:

  1. 该类基于数组实现了栈的核心功能:入栈(push)、出栈(pop)、查看栈顶(peek)、获取大小(size)、判断为空(isEmpty);
  2. #stack:ES6 新增的私有字段(Private Field) ,前缀#表示该属性只能在类内部访问 / 修改,外部无法直接操作(比如stack.#stack会报错),保证了栈的封装性(避免外部随意修改栈的底层数据);
  3. constructor():类的构造函数,创建ArrayStack实例时自动执行。初始化私有属性#stack为一个空数组,作为栈的底层存储结构(数组的 “末尾” 对应栈的 “栈顶”,因为数组的push/pop操作时间复杂度为 O (1),效率最高)。

数组实现的栈非常直观,利用了JavaScript数组的push和pop方法,实现了栈的核心功能。

三、链表实现栈

链表实现栈需要自定义节点和栈结构。

链表实现栈代码

// 链表来实现栈
class ListNode {constructor(val) {this.val = val;this.next = null; // 离散存储}
}
class LinkedListStack {#stackPeek;#size = 0;constructor() {this.#stackPeek = null;}push(num) {const node = new ListNode(num);node.next = this.#stackPeek;this.#stackPeek = node;this.#size++;}pop() {const num = this.peek();this.#stackPeek = this.#stackPeek.next;this.#size--;return num;}peek() {if (!this.#stackPeek) {throw new Error('栈为空');}return this.#stackPeek.val;}get size() {return this.#size;}isEmpty() {return this.size === 0;}
}

代码相关解释:

  1. 采用单向链表实现栈,核心逻辑是将链表头节点作为栈顶 —— 链表头增删节点仅需修改指针(时间复杂度 O (1)),完美适配栈 “后进先出(LIFO)” 的核心操作要求。

  2. 节点类(ListNode)作用:定义无序列表的最小存储单元,val存储栈元素值,next仅用于关联后继节点(离散存储),无任何排序相关逻辑,是实现无序列表的基础。

  3. 入栈方法(push):创建新节点→新节点next指向原栈顶→更新栈顶指针为新节点→栈大小 + 1,全程仅修改指针,无遍历操作,效率 O (1)。

  4. 出栈方法(pop):复用peek完成空栈校验和栈顶值获取→栈顶指针后移(原栈顶节点被垃圾回收)→栈大小 - 1→返回栈顶值,效率 O (1)。

  5. 栈顶查看方法(peek):先校验栈是否为空(空栈抛错),再返回栈顶节点的val值(仅读取,不修改栈结构),对外屏蔽无序列表的底层细节。

  6. 辅助属性 / 方法:

    size:通过 ES6 访问器属性(get)定义为只读属性,外部可获取但无法赋值篡改;

    isEmpty:基于size判断栈是否为空,语义清晰且无需重复编写空栈校验逻辑。

链表实现栈的关键在于使用#stackPeek作为栈顶指针,通过链表的链接关系实现栈的操作。

四、数组与链表实现栈的优缺点比较

时间效率

数组实现:

  • 入栈和出栈操作在尾部进行,时间复杂度为O(1)
  • 当数组容量不足时,触发扩容(复制所有元素到新数组),时间复杂度为O(n)
  • 但扩容是低频操作,平均时间复杂度仍为O(1)

链表实现:

  • 入栈操作需要创建新节点,时间复杂度为O(1)
  • 无需扩容,始终能保持O(1)的时间复杂度
  • 但链表节点需要额外操作(指针设置),在入栈时略慢于数组

空间效率

数组实现:

  • 可能造成空间浪费(如分配了100个元素空间,但只使用了50个)
  • 但内存是连续的,缓存命中率高,访问效率更好

链表实现:

  • 每个节点需要额外空间存储指针(next引用)
  • 内存不连续,缓存命中率较低
  • 但不会造成空间浪费,空间利用率更高

五、栈的实践:括号匹配问题

栈在括号匹配问题中展现出了其独特优势。这个问题要求判断字符串中的括号是否正确闭合。

const leftToRight = {"(":")","[":"]","{":"}",
}
const isValid = function(s) {if(!s) return true;const stack = []; // 栈const len = s.length; // 缓存长度for(let i = 0;i < len;i++) {const ch = s[i];if(ch === "(" || ch === "[" || ch === "{") {stack.push(leftToRight[ch]);} else {if(!stack.length || stack.pop() != ch) {return false;}}}return stack.length === 0;
}

这个实现利用了栈的FILO特性:

  1. 遇到左括号时,将对应的右括号压入栈
  2. 遇到右括号时,弹出栈顶元素进行匹配
  3. 最终栈为空表示所有括号都正确闭合

例如,对于字符串"({[]})":

  • ( → 压入)
  • { → 压入}
  • [ → 压入]
  • ] → 弹出],匹配
  • } → 弹出},匹配
  • ) → 弹出),匹配
  • 栈为空,返回true

六、总结

栈作为计算机科学中最基础、最常用的数据结构之一,其简洁性和高效性使其成为解决许多编程问题的首选工具。通过ES6的类特性,我们可以更清晰、更安全地实现栈,同时理解其背后的原理和适用场景。

在实际应用中,根据具体需求选择数组或链表实现栈:

  • 数组实现:适合数据量相对稳定、对性能要求高且内存空间有限的场景
  • 链表实现:适合数据量不确定、需要频繁动态扩展的场景

栈的实践应用远不止括号匹配问题,它在函数调用栈、表达式求值、浏览器历史记录管理等众多领域都有广泛应用。理解栈的本质——"后进先出"的特性,以及如何在不同场景下选择合适的实现方式,将使你在编程中更加得心应手。

相关新闻

  • 如何优雅地终止正在运行的TensorFlow镜像训练进程
  • 使用MLflow跟踪TensorFlow镜像中的训练实验结果
  • 科研新视界:书匠策AI解锁期刊论文写作的“隐形密码”

最新新闻

  • B站视频下载器:3个核心优势与5步实战指南
  • 性价比高的江苏优轧设备,你了解多少? - 工业推荐榜
  • 2026银川本地人必选防水补漏检测维修公司靠谱服务商TOP5推荐:房屋渗漏水检测维修/卫生间/厨房/天花板/阳台/外墙渗漏水检测补漏维修-暗管漏水检测专业仪器精准定位漏水点 - 即刻修防水
  • 江苏优轧靠谱吗?创新成果与优势深度剖析 - 工业推荐榜
  • RimWorld终极性能优化指南:用Performance-Fish告别卡顿,流畅运行200人殖民地
  • 2026麻将机十大品牌实测对比:选对免调试款省心避雷全攻略

日新闻

  • 2026速览惠州叛逆青少年学校前十大排名名单出炉 - 武汉中职最新信息发布
  • 2026上饶白蚁消杀哪家好?15年本土2大权威白蚁防治公司推荐(金盾虫控/青蚁卫士) - 我叫一
  • 天龙八部单机版终极数据管理工具:5个技巧快速掌握游戏数据编辑

周新闻

  • Visual C++运行库修复终极指南:5分钟快速解决Windows软件启动错误
  • 手把手教你构建统计局地区经济数据爬虫:从环境搭建到数据持久化全指南
  • 2026多Agent深度解析:用AI团队替代单一模型,四种架构实战落地

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号