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. 默认情况下,CentOS 7上MySQL / MariaDB的存储位置在哪里?

    Where is MySQL / MariaDB storage location by default on CentOS 7? No special configuration to the Ma ...

  2. 链接器 ld 名称的由来

    Linker 通常缩写为 LD,这是因为在 Unix 和类 Unix 系统中,ld 是链接器的常用命令名称.这个命名可以追溯到 Unix 系统的早期发展历史. 历史背景 Unix 早期:在 Unix ...

  3. LaTeX 编译警告:Script 'CJK' not explicitly supported within font 'FandolSong-Regular'. Check the typeset output, and if it is okay then ignore this warning. Otherwise a different font should be chosen.

    在编译一篇中文文档时遇到如下警告: Package fontspec Warning: Script 'CJK' not explicitly supported within font 'Fando ...

  4. USB PD和USB TYPE-C 的区别

    USB Power Delivery (USB PD) 和 USB Type-C 是两个不同但相关的技术标准,它们在功能和应用上有所区别. 1. USB Type-C 连接器标准: USB Type- ...

  5. js中判断数据类型的方法有哪些

    判断数据类型可以使用 typeof 但是typeof 判断数组和函数时返回的都是Object 不能具体判断,这时使用 instanceof 可以判断对象是否是另一个函数创造的 : 用法: typeof ...

  6. 16 Transformer 的编码器(Encodes)——我在做更优秀的词向量

    博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https:// ...

  7. 10 Self-Attention(自注意力机制)

    博客配套视频链接: https://space.bilibili.com/383551518?spm_id_from=333.1007.0.0 b 站直接看 配套 github 链接:https:// ...

  8. python中字典的运算

    问题: 如何查找在两个字典中相同的键.值元素? dict1 = {'a': 1, 'b': 2, 'c': 3} dict2 = {'a': 10, 'y': 11,'b': 2} dict1.key ...

  9. .NET云原生应用实践(二):Sticker微服务RESTful API的实现

    本章目标 完成数据访问层的基本设计 实现Sticker微服务的RESTful API 引言:应该使用ORM框架吗? 毋庸置疑,Sticker微服务需要访问数据库来管理"贴纸"(也就 ...

  10. nicegui太香了,跨平台开发和跨平台运行--使用Python+nicegui实现系统布局界面的开发

    在现今国产化浪潮的驱动下,跨平台或者缩小范围说基于国产化Linux或者基于国产鸿蒙系统的开发才是未来的趋势了,风口浪尖上,我们开发人员也只能顺势而为,本篇随笔介绍在Python开发中,使用使用Pyth ...