JavaScript 中栈的运用

在 JavaScript 中,栈(Stack)是一种非常有用的数据结构,它遵循后进先出(Last In First Out,LIFO)的原则。在本文中,我们将深入探讨栈的概念以及在 JavaScript 中的实际运用。

一、栈的概念

栈是一种线性数据结构,它只能在一端进行插入(称为入栈或压栈,push)和删除(称为出栈或弹栈,pop)操作。想象一下一摞盘子,你只能从最上面拿盘子(出栈)或者把盘子放在最上面(入栈)。

栈通常具有以下几个基本操作:

  1. push(element):将一个元素压入栈顶。
  2. pop():弹出栈顶元素并返回它。
  3. peek():查看栈顶元素,但不弹出它。
  4. isEmpty():判断栈是否为空。

二、在 JavaScript 中实现栈

以下是用 JavaScript 实现一个简单栈的代码:

class Stack {
constructor() {
this.items = [];
} push(element) {
this.items.push(element);
} pop() {
if (this.isEmpty()) {
return "Underflow";
}
return this.items.pop();
} peek() {
if (this.isEmpty()) {
return null;
}
return this.items[this.items.length - 1];
} isEmpty() {
return this.items.length === 0;
} size() {
return this.items.length;
}
}

三、栈的实际运用

(一)表达式求值

  1. 中缀表达式转后缀表达式

    在计算机科学中,将中缀表达式转换为后缀表达式是栈的一个重要应用。中缀表达式是我们通常使用的算术表达式形式,如(2 + 3) * 4。后缀表达式则是将运算符放在操作数之后,例如2 3 + 4 *

算法步骤如下:

  • 初始化一个空栈用于存储运算符。
  • 从左到右遍历中缀表达式。
  • 如果遇到操作数,直接输出。
  • 如果遇到左括号,将其压入栈。
  • 如果遇到右括号,弹出栈中的运算符并输出,直到遇到左括号,然后丢弃左括号。
  • 如果遇到运算符,根据其优先级进行处理。如果栈顶运算符的优先级高于或等于当前运算符,则弹出栈顶运算符并输出;否则,将当前运算符压入栈。
  • 遍历结束后,将栈中的剩余运算符依次弹出并输出。

以下是用 JavaScript 实现中缀表达式转后缀表达式的代码:

function infixToPostfix(expression) {
const stack = new Stack();
let postfix = "";
const precedence = {
'+': 1,
'-': 1,
'*': 2,
'/': 2
}; for (let char of expression) {
if (/[0-9]/.test(char)) {
postfix += char;
} else if (char === '(') {
stack.push(char);
} else if (char === ')') {
while (!stack.isEmpty() && stack.peek()!== '(') {
postfix += stack.pop();
}
stack.pop(); // 弹出左括号
} else {
while (!stack.isEmpty() && precedence[stack.peek()] >= precedence[char]) {
postfix += stack.pop();
}
stack.push(char);
}
} while (!stack.isEmpty()) {
postfix += stack.pop();
} return postfix;
}
  1. 后缀表达式求值

    一旦将中缀表达式转换为后缀表达式,就可以很容易地对后缀表达式进行求值。

算法步骤如下:

  • 从左到右遍历后缀表达式。
  • 如果遇到操作数,将其压入栈。
  • 如果遇到运算符,弹出栈中的两个操作数,进行相应的运算,然后将结果压回栈。
  • 遍历结束后,栈中唯一的元素就是表达式的结果。

以下是用 JavaScript 实现后缀表达式求值的代码:

function evaluatePostfix(postfix) {
const stack = new Stack();
for (let char of postfix) {
if (/[0-9]/.test(char)) {
stack.push(parseInt(char));
} else {
const operand2 = stack.pop();
const operand1 = stack.pop();
switch (char) {
case '+':
stack.push(operand1 + operand2);
break;
case '-':
stack.push(operand1 - operand2);
break;
case '*':
stack.push(operand1 * operand2);
break;
case '/':
stack.push(operand1 / operand2);
break;
}
}
}
return stack.pop();
}

(二)函数调用栈

在 JavaScript 中,当一个函数调用另一个函数时,会在内存中创建一个称为调用栈(Call Stack)的结构。调用栈是一种栈数据结构,它用于跟踪函数的调用顺序。

例如:

function functionA() {
console.log("Inside functionA");
functionB();
} function functionB() {
console.log("Inside functionB");
} functionA();

functionA被调用时,它的执行上下文被压入调用栈。当functionA调用functionB时,functionB的执行上下文也被压入调用栈。当functionB执行完毕后,它的执行上下文从调用栈中弹出。然后,functionA继续执行,直到它也执行完毕,其执行上下文也从调用栈中弹出。

这种机制确保了函数的正确执行顺序和变量的作用域管理。

(三)深度优先搜索(DFS)

深度优先搜索是一种图遍历算法,它可以使用栈来实现。

以下是用 JavaScript 实现深度优先搜索的代码:

class Graph {
constructor() {
this.adjacencyList = {};
} addVertex(vertex) {
if (!this.adjacencyList[vertex]) {
this.adjacencyList[vertex] = [];
}
} addEdge(vertex1, vertex2) {
this.adjacencyList[vertex1].push(vertex2);
this.adjacencyList[vertex2].push(vertex1);
} dfs(startVertex) {
const stack = new Stack();
const visited = {};
stack.push(startVertex);
visited[startVertex] = true; while (!stack.isEmpty()) {
const currentVertex = stack.pop();
console.log(currentVertex); for (let neighbor of this.adjacencyList[currentVertex]) {
if (!visited[neighbor]) {
stack.push(neighbor);
visited[neighbor] = true;
}
}
}
}
}

可以使用以下方式调用:

const graph = new Graph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addVertex('D');
graph.addVertex('E'); graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
graph.addEdge('B', 'D');
graph.addEdge('C', 'E'); graph.dfs('A');

在这个例子中,深度优先搜索从给定的起始顶点开始,使用栈来存储待访问的顶点。每次从栈中弹出一个顶点,访问它,并将其未访问过的邻居顶点压入栈。

(四)括号匹配

检查一个字符串中的括号是否匹配是栈的另一个常见应用。

算法步骤如下:

  • 初始化一个空栈。
  • 遍历字符串中的每个字符。
  • 如果遇到左括号,将其压入栈。
  • 如果遇到右括号,检查栈是否为空。如果为空,说明右括号没有匹配的左括号,返回 false。如果栈不为空,弹出栈顶元素,检查弹出的左括号是否与当前右括号匹配。如果不匹配,返回 false。
  • 遍历结束后,如果栈为空,说明所有括号都匹配,返回 true;否则,返回 false。

以下是用 JavaScript 实现括号匹配的代码:

function isBalanced(str) {
const stack = new Stack();
for (let char of str) {
if (char === '(' || char === '[' || char === '{') {
stack.push(char);
} else if (char === ')' || char === ']' || char === '}') {
if (stack.isEmpty()) {
return false;
}
const top = stack.pop();
if ((char === ')' && top!== '(') || (char === ']' && top!== '[') || (char === '}' && top!== '{')) {
return false;
}
}
}
return stack.isEmpty();
}

四、总结

栈是一种强大的数据结构,在 JavaScript 中有许多实际应用。从表达式求值到函数调用栈,从图的遍历到括号匹配,栈都发挥了重要作用。理解栈的概念和操作,以及如何在 JavaScript 中实现和应用栈,对于编写高效的代码和解决各种编程问题非常有帮助。

js中栈的运用的更多相关文章

  1. 在JS中关于堆与栈的认识function abc(a){ a=100; } function abc2(arr){ arr[0]=0; }

    平常我们的印象中堆与栈就是两种数据结构,栈就是先进后出:堆就是先进先出.下面我就常见的例子做分析: main.cpp int a = 0; 全局初始化区 char *p1; 全局未初始化区 main( ...

  2. js 中的栈和堆

    js中的栈与堆的讲解/基本数据类型与引用类型的讲解 前言:1. 学习前端,入门简单,想学好确实是一件很困难的事情,东西多而且杂,版本快速迭代,产品框架层出不穷. 2. 前端学习成本确实很高,需要不断的 ...

  3. js中的栈、堆、队列、内存空间

    栈(stack) .堆(heap). 队列(queue)是js的三种数据结构. 栈(stack) 栈的特点是"LIFO,即后进先出(Last in, first out)".数据存 ...

  4. 浅析JS中的堆内存与栈内存

    最近跟着组里的大佬面试碰到这么一个问题, Q:说说var.let.const的区别 A:balabalabalabla... Q:const定义的值能改么? A:你逗我?不能吧 不知道各位看官怎么想? ...

  5. js中的堆内存和栈内存

    我们常常会听说什么栈内存.堆内存,那么他们到底有什么区别呢,在js中又是如何区分他们的呢,今天我们来看一下. 一.栈内存和堆内存的区分 一般来说,栈内存主要用于存储各种基本类型的变量,包括Boolea ...

  6. JS高级面试题思路(装箱和拆箱、栈和堆、js中sort()方法、.js中Date对象中的getMounth() 需要注意的、开发中编码和解码使用场景有哪些)

    1.装箱和拆箱: 装箱:把基本数据类型转化为对应的引用数据类型的操作: var num = 123 // num var objNum = new Num(123) // object console ...

  7. python 全栈开发,Day52(关于DOM操作的相关案例,JS中的面向对象,定时器,BOM,client、offset、scroll系列)

    昨日作业讲解: 京东购物车 京东购物车效果: 实现原理: 用2个盒子,就可以完整效果. 先让上面的小盒子向下移动1px,此时就出现了压盖效果.小盒子设置z-index压盖大盒子,将小盒子的下边框去掉, ...

  8. python 全栈开发,Day54(关于DOM操作的相关案例,JS中的面向对象,定时器,BOM,client、offset、scroll系列)

    04-jQuery的属性操作 jquery的属性操作模块分为四个部分:html属性操作,dom属性操作,类样式操作和值操作 html属性操作:是对html文档中的属性进行读取,设置和移除操作.比如at ...

  9. 关于js中的this

    关于js中的this this是javascript中一个很特别的关键字,也是一种很复杂的机制,学习this的第一步就是要明白this既不指向函数自身也不指向函数的词法作用域,this实际上是函数被调 ...

  10. JS 中没有按地址(引用)传递,只有按值传递

    很多人,包括我,受书本知识消化不彻底的影响,认为 JS 中参数有两种传递方式:数字.字符串等按值传递:数组.对象等按地址(引用)传递.对此种观点,我们要谨慎. var v1 = [] var v2 = ...

随机推荐

  1. docker安装cdh6.3.2集群(联网版)

    一.前言 本次采用的在线安装方式,cdh为6.3.2版本,系统为centos7.4, docker节点可以为任意多个,下文将以3个docker容器为示例进行展示.此方法也可用在docker swarm ...

  2. java_基本类型

    1. 所有的基本类型都是小写 例如  int 2. 所有的类  类型首字母大写  例如String

  3. Docker网络中篇-docker网络的四种类型

    通过上一篇学习,我们对docker网络有了初步的了解.本篇,咱们就来实战docker网络. docker网络实战 实战docker网络,我们将从以下几个案例来讲解 1:birdge是什么? 2:hos ...

  4. MyBatis日志工厂

    目录 日志工厂 标准日志实现 Log4j 使用步骤: 日志工厂 我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率? 如果一个 数据库相关的操作出现了问题,我们可 ...

  5. HttpContext.SignInAsync 失效(表面解决了问题,未深入到.net core 源码去找问题,记录一下,等有时间翻一下.net core 源码试试能不能找到根本原因)

    今天在弄 identityServer4 项目的时候,发现好好的登录竟然没用了. 各种跟踪后发现是 HttpContext.SignInAsync 这个方法不写cookies了 原本经过这个方法后,会 ...

  6. parser.add_argument

    parser.add_argument 在解析参数时,有个地方很值得注意. --dict-name,会把dict-name解析为变量dict_name.也就是说会把破折号转成下划线.

  7. 【笔记】前端人脸检测之clmtrackr.js的使用

    clmtrackr.js使用示例代码 html代码: <div class="video-con"> <video id="video" pl ...

  8. Angular 18+ 高级教程 – Angular Compiler (AKA ngc) Quick View

    前言 在 Get Started 那一篇,我们提到过 Angular Compilation.这篇稍微给点具体画面,让大家感受一下. 但不会讲细节,对细节感兴趣的可以看这篇 Medium – How ...

  9. Git Bash OpenSSL – Generate Self Signed Certificate

    前言 以前就写过了, 只是写的太乱, 这篇是一个整理版. 以前的文章: Git Bash 创建证书 PowerShell 创建证书 我已经没有用 PowerSheel 做证书了, 所以就不介绍了. 参 ...

  10. oneforall配置环境,报错cannot import name 'sre_parse' from 're' 解决方法

    高版本python中re模块没有了sre_parse模块, 可以修改python中的exrex.py 代码,直接导入sre_parse模块