栈问题归纳

  • 栈和队列一样也是受限制的线性表,它的特征是“先进后出,后进先出”,常常被用于一些优先级有差异的等待中。

  • C++的标准库为我们提供了栈的使用,下面代码示例栈的一些常用方法。

#include <stack>
#include <cstdio>
using namespace std;
int main(){
    stack<int> myStack;//初始化栈,栈中元素都是int类型
    printf("size of myStack = %d\n", myStack.size());//栈中元素个数
    for (int i = 0; i < 10; ++i) {
        myStack.push(i);//入栈
    }
    printf("size of myStack = %d\n", myStack.size());
    while (!myStack.empty()){//栈是否为空
        printf("cur top = %d\n", myStack.top());//打印栈顶元素
        myStack.pop();//出栈
    }
    printf("myStack is empty!\n");
}
  • 例题1 括号匹配

描述

在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用"$"标注,不能匹配的右括号用"?"标注.

输入

输入包括多组数据,每组数据一行,包含一个字符串,只包含左右括号和大小写字母,字符串长度不超过100

注意:cin.getline(str,100)最多只能输入99个字符!

输出

对每组输出数据,输出两行,第一行包含原始输入字符,第二行由"$","?"和空格组成,"$"和"?"表示与之对应的左括号和右括号不能匹配。

样例输入

((ABCD(x)

)(rttyy())sss)(

样例输出

((ABCD(x)

$$

)(rttyy())sss)(

? ?$

题目分析

(1)我们会发现,每当左括号遇到一个右括号,他们俩就匹配成功了,那我们正好可以用栈来解决,遇到左括号我们就压栈,遇到右括号就把栈中的左括号弹栈;

(2)那如何判别非法呢?左括号非法即没有右括号匹配,即它到最后还在栈中,我们可以换种思维,一开始把入栈的左括号都视为非法,每当它匹配出栈后就合法了;而右括号非法更加容易,它没有左括号匹配,即此时放左括号的栈已为空栈。

代码示例

//括号匹配
#include <stack>
#include <cstdio>
#include <string>
using namespace std;
int main(){
    char buf[200];
    while (fgets(buf,200,stdin) != NULL){
        //fgets配合while实现不确定数量的多行读取
        string str = buf;
        str.pop_back();//str去掉了额外的换行

        stack<unsigned> indexStack;//存储了左圆括号的下标
        string res;//保存输出的结果
        for(unsigned i = 0; i < str.size(); ++i){
            if(str[i] == '('){
                indexStack.push(i);// !!!把下标压栈
                //都先认为这个左圆括号是非法的
                res.push_back('$');
            }
            else if(str[i] == ')'){
                if(indexStack.empty()){
                    res.push_back('?');//栈空,即没有左括号与其匹配
                }
                else{
                    res.push_back(' ');
                    res[indexStack.top()] = ' ';// 把$改为空格,将左括号合法
                    indexStack.pop();//匹配成功,该左括号出栈
                }
            }
            else{
                res.push_back(' ');//其他非括号的字符用空格代替
            }
        }
        printf("%s\n%s\n",str.c_str(),res.c_str());
    }
}
  • 例题2 简单计算器 (浙江大学复试上机题)

描述

读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。

输入描述:

测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。

输出描述:

对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。

输入:

1 + 2

4 + 2 * 5 - 7 / 11

0

输出:

3.00

13.36

题目分析

(1)对于计算表达式的问题是应用到栈最多的问题,因为对于加减乘除这种优先级差异的非常适合用栈。这类问题我们需要两个栈,一个是存储数字的num栈,一个是存储运算符的oper栈

(2)每当要入栈的运算符优先级比栈顶运算符高时,就继续将这个运算符压栈(因为乘除法比加减法先运算,所以之前如果有加减法我们就先不算,要把乘除号入栈,以便之后弹栈时先算乘除法);当要入栈的运算符优先级比栈顶运算符低或相等时,就先把栈顶运算符弹出,并在num栈取(弹)两个数,进行运算后把结果压入num栈,然后再把之前要入栈的运算符入栈(例如前面进栈了一个除法,此时要入的是乘或者加减,都不用入,因为按从左到右和先算乘除来看,就应把栈中的除法先算完)。

(3)这个题还有一个要注意的点是如何存输入的数?我们一开始输入的是一串数字加符号的字符串,但我们发现每个数之后都有一个空格,因此我们可以每次把空格前的字符串保存下来,然后用stod() 方法把字符串变为数。此外,最后一个数的后面没有空格,所以我们需要添加一个虚假的终止符$,用同样的方法存储最后一个数。

代码示例

//简单计算器
#include <stack>
#include <string>
#include <cstdio>
#include <map>
using namespace std;
int main(){
    char buf[300];
    //map用来存储优先级
    map<char,int> priority = {
            {'$',0},
            {'+',1},{'-',1},
            {'*',2},{'/',2},
    };
    while(fgets(buf,300,stdin) != NULL){
        string expr = buf;
        expr.pop_back();

        if(expr == "0"){
            break;
        }
        expr.push_back('$');//补充一个虚拟的终止符

        string num; //用来搜集单独的0-9以组成一个数字 1 2 3 4 --》 1234
        stack<double> numstack;
        stack<char> operstack;
        for (unsigned i = 0; i < expr.size(); ++i) {
            if(expr[i] >= '0' && expr[i] <= '9'){
                num.push_back(expr[i]);
            }
            else if(expr[i] == ' '){
                if(num != ""){
                    numstack.push(stod(num));//stod --> string to double
                    num = "";
                }
            }
            else{
                // +-*/$
                if(expr[i] == '$'){
                    if(num != ""){
                        numstack.push(stod(num));//stod --> string to double
                        num = "";
                    }
                }
                //计算比expr[i]优先级更高的运算符
                while (!operstack.empty()&&priority[operstack.top()] >= priority[expr[i]]){
                    //新来的运算符的优先级不高于栈顶的优先级
                    char oper = operstack.top();
                    operstack.pop();
                    double rhs = numstack.top();
                    numstack.pop();
                    double lhs = numstack.top();
                    numstack.pop();
                    switch (oper) {
                        case '+':
                            numstack.push(lhs + rhs);
                            break;
                        case '-':
                            numstack.push(lhs - rhs);
                            break;
                        case '*':
                            numstack.push(lhs * rhs);
                            break;
                        case '/':
                            numstack.push(lhs / rhs);
                            break;
                    }
                }
                operstack.push(expr[i]);
            }
        }
        printf("%.2f\n",numstack.top());
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~許諾~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值