浅谈javascript中的栈

326 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

大家好,我是尘心,这次想跟大家浅谈一下我对javascript中栈的理解。

什么是栈?

在计算机的定义上,栈(stack)又名堆栈,它是一种运算受限的线性表。
限定仅在表尾进行插入和删除操作的线性表。是一种特殊的结构体,具有指针指向栈顶的。

7bcdc5488afe49b986b4f316d25bf878.png

然而,在javascript中的栈不像是一种构造,反而更像是一种算法, 一种为了应对特殊情况而诞生的算法,而我们习惯称之为“栈”! (小尘对于栈的理解,有其它想法的可以评论区留言)

之所以这么说,是因为javascript语言中并没有强制的定义栈的构造,

浏览器引擎也没有强制的执行栈只能从栈顶进且从栈顶出的命令

而究其原因则是javascript中的栈并不像其它语言里面用特殊的方法构造一个栈

javascript里的栈就是一个数组,一个被我们主观意识认定为每个数据只能从栈顶进且栈顶出的数组

这么说可能大家还是不太理解什么是栈,而什么又是栈顶进且栈顶出,那请大家跟小尘来看:

所谓栈顶,(javascript中)就是数组的末尾:

而栈顶进便是所谓的入栈

可以看到下图,第一次,我往该数组末尾入栈了一个5,在此之前,这还是一个空数组

而第二次则是入栈了一个10,即a.push(10),第三次则是入栈了一个15

代码实现如下:

let a=[];
a.push(5);                 //第一次入栈
a.push(10);                //第二次入栈
a.push(15);                //第三次入栈
每次只能从栈顶添加

2-1Q204160T51J.gif

而栈顶出则是所谓的出栈

如下图: 代码实现:

a.pop();                  //第一次出栈
a.pop();                  //第二次出栈
a.pop();                  //第三次出栈
每次只能从栈顶移除

2-1Q20416091E32.gif

之所以说javascript中的栈不像是其他语言中的有着极大限制的线性构造表,则是因为javascript中的栈就是由数组构成,倘若你不想遵守栈的规则,那么这就是一个普通的数组,没有半点特别,而说javascript中的栈更像是一种算法则是因为栈的定义以及特性可以完美的给我们解决一些特殊的情况,就像动态规划,以及回溯法在面对特殊情况下的妙用。

接下来我们来看一个题目,大家就可以理解了

题目如下:

QQ图片20220708000447.png

QQ图片20220708000456.png

按照我们的理解这题该怎么做呢

我猜想大家第一时间想的应该是双重for循环嵌套吧,可这样不说能不能成,即便写过了也毫无亮点

当然,小尘第一时间想的也是双重for循环嵌套使用,ahahaha~

但这题有着更简便的方法,那就是栈了

先把代码晾这(详解也在一旁有注释,代码区左右可滑动,右边是解释以及注释):

var isValid = function(s) { 
    const stack = [];                    //定义一个数组,这里是指栈
    if(s.length % 2 == 1) {  
        return false;                    //判断这个数组是奇数个还是偶数个,奇数个则可以直接返回false了
    }
    for(let i = 0; i < s.length; i+=1) {          //对题目给定的字符串进行遍历以及对栈进行入栈以及出栈操作
        const c = s[i];
        if(c === '{' || c === '(' || c === '[') {         //如果为左半边括号,则进行入栈
            stack.push(c);
        } else {  
            const t = stack[stack.length - 1];            //如果不是三种左半边括号则对其进行比对
            if( 
                (t === '(' && c === ')') ||      //作为一个有效括号,左半边括号它配对的一定在它的右边,
                (t === '{' && c === '}') ||      //且每次读取到右半边括号部分,它的上一位
                (t === '[' && c === ']')         //即目前栈顶所存在的那个左半边一定能与其配对
            ) {                                  //一旦配对成功,则将栈顶的那一个左半边括号进行出栈
                stack.pop();                     //方便其它的括号继续比对,而下面一个左半边括号则成为新的栈顶
            } else { 
                return false ;                   //如果比对失败,则直接返回false
            }
        }
    }
    return stack.length === 0;
}

isValid('{()[]}')

第一步const stack = [];是为了创建一个栈(javascript里面就是数组啦)

第二步if(s.length % 2 == 1) { return false; }的目的就是为了判断题目传给我们的数组是奇数个还是偶数个数,如果为奇数,那便确定最后不管怎么样,总是有没办法配对的半边括号

第三步就是咱们的正片啦,栈的运用也在此:

首先,对这个函数传进来的字符串进行遍历

倘若为左半边括号,则往栈顶入栈(即数组最后面,上面有解释啦)

倘若为右半边括号,则与栈顶元素进行判断是否配对,如果没配对,则返回false,配对成功则将栈顶元素那个左半边的括号进行出栈(至于为什么最里面的左括号一定有一个右半边括号进行配对,这个大家可以自己“悟”一下哈,就是一种规律啦)

图解如下:

微信图片_20220709184439.png

微信图片_20220709184923.png

微信图片_20220709185143.png

微信图片_20220709185441.png

微信图片_20220709185545.png

微信图片_20220709185652.png

微信图片_20220709185808.png

这里实在是太困了,这一题简略解释在代码旁边,有需要详细讲解的可以私聊,回头小尘也是会补上的。(已补上!)

所以在小尘的主观意识里,javascript里的栈更像是一种帮我们解除特殊情况的一种算法,而不是一种特殊构造。

浅谈javascript中的栈就到这啦,小尘也是一名正在学习的人,可能表达不是那么准确,望多多指教哈!