当前位置: 首页 > 后端技术 > Node.js

数据结构知不知道系列-堆栈

时间:2023-04-04 00:39:14 Node.js

希望每次回忆起来,都不要对人生感到愧疚。——郭小川栈,英文LastInFirstOut简称LIFO,遵循后进先出的原则,与“队列”相反,在栈的头部添加和删除元素,如果没有元素在栈,则称为空栈。作者简介:MayJun,NodejsDeveloper,90后,热爱技术,热爱分享,公众号《Nodejs技术栈》,Github开源项目https://www.nodejs.redStack简介也是一个现实生活中的很多场景,比如叠盘子,从上往下一张一张放,拿出来的时候再从上往下一张一张拿走。不可能直接从底部取出它们。如下图所示,这也是栈的一个典型应用。通过这个例子,也可以得出栈的两个特点:数据只能从栈顶访问。数据访问遵循后进先出原则。栈的运行机制。通过前面的学习,你应该对栈的概念有了初步的了解。在这里,我们将从零开始实现一个栈,再进一步。分析一下栈的运行机制,看看实现栈需要哪些步骤:Constructor(capacity):初始化栈内存空间,设置栈的容量isEmpty():判断栈是否为空,是否有元素isOverflow():检查堆栈空间是否已满。满则不能入栈enStack(element):栈顶位置,先判断栈是否满deStack():弹出栈顶位置,先判断栈元素是否为空len():栈空间已有元素长度clear():清空栈元素,内存空间仍保留需要手动回收)traversing():遍历输出栈元素初始化栈空间,声明它在构造函数的构造函数中,传入初始化栈空间的容量,将栈顶(top)初始化为0,不用注意,栈底一直为0。/****@param{Number}capacity栈空间容量*/constructor(capacity){if(!capacity){thrownewError('需要容量字段!');}this.capacity=容量;这。堆栈=新数组(容量);这个.top=0;//初始化栈顶为0}检查栈空间是否为空定义isEmpty()方法返回栈空间是否为空,根据top栈顶的位置判断。isEmpty(){返回this.top===0?true:false;}检查栈空间是否溢出定义isOverflow()方法返回栈空间是否溢出,根据栈顶的位置和栈的空间容量进行判断。isOverflow(){returnthis.top===this.capacity;}栈定义enStack(element)方法进行入栈操作,element是传入入栈的参数,入栈前先判断栈是否满,如果入栈未满,可入栈,最后入栈位置为++操作。/***入栈*@param{*}element入栈元素*/enStack(element){if(this.isOverflow()){thrownewError('stackisfull');}这个堆栈[这个。顶部]=元素;this.top++;}pop定义enStack(element)方法出栈,先判断栈空间是否为空,不为空则出栈,这里注意出栈位置,因为元素被压入thestack之后会进行++操作,所以出栈时当前栈位置必须没有元素,需要先进行--操作。deStack(){if(this.isEmpty()){thrownewError('堆栈为空');}this.top--;returnthis.stack[this.top];}栈元素的长度很好判断,根据栈顶位置信息,可以len(){returnthis.top;}清除栈元素有几种这里的实现,也可以初始化栈空间,或者设置栈顶位置为0。clear(){this.top=0;}栈销毁在一些高级语言中,会有垃圾回收机制。比如在JS中,只要当前对象不再持有引用,下次垃圾回收到来时,它就会被回收。不清楚的可以看看我之前写的Node.js内存管理和V8垃圾回收机制。destroy(){this.stack=null;}栈元素遍历定义了traversing(isBottom)方法来遍历并输出栈的元素。默认为顶部遍历,也可以传入isBottom参数为true,从底部开始遍历。遍历(isBottom=false){constarr=[];if(isBottom){for(leti=0;i=0;i--){arr.push(this.stack[i])}}console.log(arr.join('|'));}dosomething测试是做下一个测试看推送,pop和遍历操作。您可以在练习过程中自行练习其他功能。consts1=newStackStudy(4);s1.enStack('Nodejs');//压入堆栈s1.enStack('Technology');s1.enStack('Technology');s1.enStack('Stack');s1.traversing()//stack|技术|技术|Nodejsconsole.log(s1.deStack());//pop->stacks1.traversing()//技术|技术|Nodejss1.traversing(true)//来自遍历栈底:Nodejs|技术|技术下图展示了上述程序的出栈过程的运行机制。栈的源码地址如下:https://github.com/Q-Angelo/project-training/tree/master/algorithm/stack.jsJavaScript数组实现栈JavaScript中提供的数组函数可以实现一个简单的栈,而且使用起来非常方便,只要熟悉了相关的API,下面就来看看基于JS数组的入栈出栈的实现过程。上图展示了栈初始化、入栈、出栈的过程。接下来我们使用JavaScript原型链来实现。初始化队列初始化一个存储栈元素的数据结构,如果不传入,默认赋值为空数组。函数StackStudy(元素){this.elements=元素||[];}添加栈元素实现一个enStack方法,向栈中添加元素,注意只能在栈头添加,在JavaScript数组中使用push方法。StackStudy.prototype.enStack=function(element){this.elements.push(element);}移除栈元素实现一个deStack方法,从栈尾弹出元素,在JavaScript数组中使用pop方法(这是不同于队列)。StackStudy.prototype.deStack=function(){returnthis.elements.pop();}通过JavaScript数组实现非常简单,源码如下:https://github.com/Q-Angelo/project-training/tree/master/algorithm/stack-js.jsstack的经典应用通过前面的讲解,相信对stack有了一定的了解,那么stack可以用来做什么呢,本节给出几个典型的应用案例。十进制与二进制、八进制、十六进制的转换现在我们生活中用十进制来表示最多,也是人们最容易理解和记忆的,但是计算机在处理的时候,必须要转换成二进制进行计算.在十进制和二进制之间的转换过程中,一般使用八进制或十六进制作为二进制的缩写。所以这里主要讲解十进制、八进制、十六进制、二进制转换过程中在栈中的实际应用。首先,你需要了解这些数据类型之间的转换规则,通过一张图告诉你并不难。上图中,我们用十进制除以要转换的数据类型(二进制、八进制、十六进制),余数入栈。用代码实现这个原理非常简单。代码constStackStudy=require('./stack.js');conststr='0123456789ABCDEF';functiondataConversion(num,type){让x=num;consts1=newStackStudy(20);while(x!=0){s1.enStack(x%type);x=Math.floor(x/类型);}while(!s1.isEmpty()){console.log(str[s1.deStack()]);}安慰。日志('----------');return;}参考我们在栈的运行机制中讲解的代码,编写dataConversion方法,压栈,出栈,做遍历输出。代码中定义的变量str是针对字母以十六进制出现的情况。下面的测试结果完全符合我们的预期,你也可以用电脑的计算器功能来验证一下。//测试八进制dataConversion(1024,8);//2000//测试十六进制dataConversion(1024,16);//400//测试带字母的十六进制dataConversion(3000,16);//BB8//测试二进制数据Conversion(1024,2);//将10000000000十进制转换为二进制、八进制、十六进制源码地址:https://github.com/Q-Angelo/project-training/tree/master/algorithm/stack-data-conversion.js平衡括号这是另一个实用的问题。在某些操作中,您可能写过以下表达式。为了保证运算的顺序,使用了括号,但是要注意括号一定要平衡。每个左括号必须对应一个右括号,否则程序将不能正常运行((1+2)*3*(4+5))*6上面例子组成的平衡表达式(()())不平衡表达式(()()通过“栈”解决括号平衡问题实现步骤初始化一个空栈{1}遍历待检测符号{2}遍历需要检测的平衡符号{3}如果字符属于入栈的符号([{(...)入栈{3.1}如果字符属于闭合符号,先判断栈空间是否为空,为空则中断运行,否则出栈,如果出栈的字符不是闭合符号对应的开符号,则检测失败,中断操作跳出循环{3.2}每次循环完成判断当前中断是否完成,如果操作已经中断,将非法字符入栈,中断最外层字符检测循环{4},最后检测栈是否为空,为空则通过,否则不输出{5}编码.可以参考《通过“栈”解决平衡括号问题的实现步骤》,有助于理解下面的代码constStack=require('./stack');constdetectionStr='[]()';//定义需要检测的余额符号,如何按照这个格式定义其他符号functiontest(str){letisTermination=false;//是否终止,默认为falseletstack=newStack(20);//初始化堆栈空间{1}for(leti=0;i