数据结构之队列和栈的应用

本文介绍队列和栈的实际应用:括号匹配、表达式求值

在这里插入图片描述


一、括号匹配问题

【问题描述】:给定一个仅含有括号的字符串,如何去判断该括号序列是否合法呢?
例如:()()合法,(())合法,【()】合法
但((()不合法,【)【)不合法

用栈来解决。

在这里插入图片描述
下面开始模拟算法,走两遍


比如第一种情况:【()】【】

参考上面的算法思想,执行过程:
1、先看第一个括号,因为是左括号,所以直接把第一个括号【,压入栈中。
2、再看第二个,也是左括号(,所以直接扔进栈就行
3、第三个括号是右括号),所以我们要弹出一个栈顶元素 ( 进行匹配,发现两者正好匹配,然后继续。现在栈中就只有一个元素【了。
4、第四个括号是】,同上,因为是右括号,所以弹出栈顶元素,然后发现是匹配的。继续,现在栈中没有元素了。
5、第五个是左括号,入栈
6、第六个是右括号,同三,匹配。现在栈中又没有元素了
7、字符串读取完毕,且此时栈中为空,所以字符串是满足括号匹配的!


情况2、字符串:【()

执行过程:
1、第一个括号为左括号,直接入栈
2、第二个括号为左括号,直接入栈
3、第三个括号为),弹出栈顶元素(,并检查是否匹配,发现两者匹配,因此继续。现在栈中元素为【
4、字符串读取完毕,但栈不空,所以该括号字符串不满足括号匹配!


情况3、字符串:【(】【】

执行过程:
1、第一个括号为左括号,直接入栈
2、第二个括号为左括号,直接入栈
3、第三个括号为右括号,所以弹出栈顶元素(,检查发现(和】是不匹配的,因此算法直接return false。该字符串不匹配。


二、表达式求值

先介绍三种表达式:
在这里插入图片描述
那么复杂的表达式,如何完成中缀转前缀呢?

中缀转前缀

核心思想,比如(A+B)*C
我们先把括号内部优先级高的,A+B转成前缀:+AB
然后把(+AB) *C种的+AB看作一个数
因此,最后的转换结果是:
*+ABC

在这里插入图片描述


中缀转后缀

在这里插入图片描述
思路和转前缀一样


用栈实现的算法模拟:

我们以中缀转后缀为例子

在这里插入图片描述

执行过程:
1、检测到是左括号(,入栈
2、同上,左括号入栈
3、检测到是数字A,于是将其抄写下来,此时后缀表达式为:A
4、运算符+,根据规则c,运算符优先级是高于左括号的,直接入栈
5、然后检测到数字B,于是抄写下来,此时后缀表达式为:AB
6、遇到右括号了,注意规则b,我们将栈中的运算符全部弹出加入到表达式中,然后直到遇到左括号,将其删除。此时后缀表达式为:AB+。栈中此时为:(
7、遇到*,乘号优先级比括号高,直接压到栈里面。
8、遇到数字C,将其抄写下,此时后缀表达式为:AB+C
9、遇到右括号,将乘号弹出,此时后缀表达式为:AB+C*。并且根据规则b,还要将栈中的左括号删除。那么此时栈中就空了
10、遇到-,因为栈空,直接入栈,
11、遇到左括号,加入栈中
12、遇到数字E,抄写下来,此时后缀表达式为:AB+C*E
13、遇到减号-,因为栈顶是(,所以直接入栈,此时栈内为:- ( -
三个元素
14、遇到数字F,抄写,此时后缀表达式为:AB+C * EF
15、遇到右括号,将减号弹出,加入到表达式中,并且把左括号删除
此时后缀表达式为 AB+C * EF-
16、根据规则d,遍历完成,将栈中所有元素弹出,加入到表达式里。于是弹出减号,此时后缀表达式为 AB+C * EF–

得到最后的答案。

这个算法手动模拟起来稍微麻烦一点,大家多试试,弄熟就好了


后缀表达式求值

那么我们幸幸苦苦学会了中缀表达式转后缀表达式,到底是为了什么呢?相信很多读者看到这里都会有这样的疑问。哈哈,其实就是因为用后缀表达式来求值是一件非常简单的事情!而中缀表达式交给计算机去直接算,那可就麻烦了。

因此,我们回了中缀转后缀之后,就可以将复杂的中缀表达式先转成后缀表达式,然后再用后缀表达式的求值算法来计算出最后的值。

那,说了这么多,后缀表达式求值到底有多简单呢?

算法思想
利用一个栈,每次把操作数扔进栈,遇到一个运算符就把栈顶两个元素弹出做一个运算,然后再把运算结果压到栈中,循环以上过程,最后在栈里会得到一个数字,就是结果。

说的有点抽象,来个例子:
比如我们以上面那个后缀表达式为例子:
AB+C*EF–
比如我们取A = 3;B = 2;C = 2;E = 6;F = 4
那么根据其对应的中缀表达式【(A + B) * C】 - 【E - F】
我们可以算出,结果为8
下面我们用后缀表达式算法检验一下:

1、遇到数,无脑扔进栈。所以AB直接扔进,现在栈中元素:AB
2、遇到+,于是将栈顶两个元素AB弹出,做运算A+B = 3 + 2 = 5,将结果5压入栈
3、遇到C,直接入栈
4、遇到*,于是将C、5弹出,做运算C*5 = 5 * 2 = 10;将10压入栈
5、遇到E、F,直接无脑入栈,那么现在栈中:10, E, F三个元素
6、遇到-,将E、F弹出,做运算E - F = 6 - 4 = 2;将2压入栈
7、遇到-,将10、2弹出,做运算10 - 2 = 8。
8、字符串遍历完毕,栈中仅有的是结果,所以结果为8,经检验,算法正确!


所以今后做表达式求值算法的思路就很明确了,先将中缀表达式转后缀,然后再用后缀表达式求值算法求出结果即可。人比较擅长中缀表达式求值,但计算机更喜欢后缀表达式运算


【js代码实现】:
只针对+ - 的情况

 //中缀转后缀,然后后缀求值
var calculateBack = function(s){
    var stack = [];
    var arr = s.split("");
    for(let i = 0;i < arr.length;i++){
        if(arr[i] == '+' || arr[i] == '-')  continue;
        arr[i] = Number.parseInt(arr[i]);
    } 
    for(let i = 0;i < arr.length;i++){ 
        if(arr[i] == '+' || arr[i] == '-'){
            let x = stack[stack.length - 1];
            stack.pop();
            let y = stack[stack.length - 1];
            stack.pop();
            if(arr[i] == '+'){
                let val = x + y;
                stack.push(val);
            }else{
                let val = y-x;          //注意减法顺序不要反了
                stack.push(val);
            }
        }else{
            stack.push(arr[i]);
        }
    }
    return stack[0];
}

//中缀转后缀
var midToBack = function(s){

    var res = [];
    var temp = s.trim().split("");
    var arr = [];
    for(let i = 0;i < temp.length;i++){
        if(temp[i] != ' '){
            arr.push(temp[i]);
        }
    } 
	
	//下面为核心,上面都是预处理字符串
    var stack = [];
    for(let i = 0;i < arr.length;i++){
        if(arr[i] == '('){
            stack.push(arr[i]);
        }else if(arr[i] == ')'){
            while(stack[stack.length - 1] != '('){
                res.push(stack[stack.length - 1]);
                stack.pop()
            }
            stack.pop();
        }else if(arr[i] == '+' || arr[i] == '-'){
            if(stack.length != 0 && stack[stack.length - 1] != '('){
                while(stack.length != 0 && stack[stack.length - 1] != '('){
                    res.push(stack[stack.length - 1]);
                    stack.pop()
                }
            }
            stack.push(arr[i]);
        }else{
            res.push(arr[i]);
        }
    }
    //栈中剩余元素直接加过去
    while(stack.length != 0){
        res.push(stack[stack.length - 1]);
        stack.pop()
    } 
    return res.join("");
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值