当前位置: 首页 > article >正文

c++_栈习题

编排字符串

3575. 编排字符串 - AcWing题库

题目:请输入字符串,最多输出 4 个字符串,要求后输出的字符串排在前面

输入: 第一行为字符串个数 m。

        接下来 m行:字符串s。

输出:

每有一行字符串的输入, 就要有1行的输出

对应有m行,每行:最多输出 4 个字符串

思路:

输出中要求后输出的字符串排在前面,故我们需要使用栈;

1)定义一个栈myStack;将输入的各个字符串压入其中

2)在每一个字符串输入之后,再定义另一个栈outputStack,接收当前myStack栈中的值,弹栈操作便在这个outStack中执行。(这样就能不破坏myStack的结构);

#define  _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include <stdio.h>
#include<string>
#include<stack>
using namespace std;
int main() {
    int m;
    scanf("%d", &m);
    stack<string> myStack;
    for (int i = 0; i < m; i++) {
        string s;
        char arr[1024];
        scanf("%s", arr);
        s = arr;
        myStack.push(s);
        stack<string>outputStack = myStack;
        for (int j = 1; j <= 4; j++) {
            if (outputStack.empty()) {
                break;
            }
            printf("%d=%s ", j, outputStack.top().c_str());
            outputStack.pop();
        }
        printf("\n");
    }
    return 0;
}

代码分析

1.这道题最需要学习的是他的输入方式;即他是每输入一个字符串就需要打印一次;我们需要输入m个字符串;所以使用一个for(int i。。。。)循环,然后在该循环中进行处理+输出。

2.从输出样例我们可以看到,每一轮的输出过后栈的元素并没有被弹出;但是输出内容却又需要按照先进先出的形式在进行(即得先弹出上一个才能输出下一个),这种情况上面代码想到了什么工具?

==》使用一个副本 stack<string>outputStack在每一轮for(int i......)中来复制栈中的内容,弹栈的操作就由这个副本来进行。

括号匹配

输入: 共一行,包含一个由 <,(,{,[,>,),},] 构成的字符串。

输出:括号若正确匹配;则输出yes,否则输出no

处理:判断括号是否匹配

题目中有4种括号:

小括号: ( ) 中括号: [ ] 大括号: { } 尖括号: < >

对于一组括号来说: 左右括号的数量要相等,顺序要正确(左括号在前, 每个右括号要匹配最近的未匹配的左括号 )具体来说:

对于([])

后面的[]符号需要优先匹配;所以我们可以借助栈的结构;我们可以这样做:

遇到左括号时压栈;遇到右括号弹栈;且如果有以下情况,便能说明括号不匹配

1)栈为空,遇到右边括号

2)栈非空,遇到右括号,但是左右括号不匹配;

3)扫描完字符串,栈非空(即存在还没匹配的左括号)

#define  _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<string>
#include<stack>
using namespace std;
​
​
/*判断当前字符属于左括号还是右括号
参数:字符
返回值:int
*/
int isWhat(char c) {
    if (c == '<' || c == '(' || c == '{' || c == '[') {
        return -1;
    }
    else if (c == '>' || c == ')' || c == '}' || c == ']') {
        return -2;
​
    }
    else {
        return -3;
    }
}
​
bool isMatch(char left, char right) {
    if (left == '(' && right == ')'
        || left == '<' && right == '>'
        || left == '{' && right == '}'
        || left == '[' && right == ']') {
        return true;
    }
    else {
        return false;
    }
}
int main() {
    string s;
    char arr[10000];
    scanf("%s", arr);
    s = arr;
    //定义一个栈
    stack<char> myStack;
    //扫描输入的字符串
    for (int i = 0; i < s.size(); i++) {
        //遇到左括号就输将之压栈
        if (isWhat(s[i]) == -1) {
            myStack.push(s[i]);
        }
        if (isWhat(s[i]) == -2) {
            //右括号
            //若栈为空,或当前扫描到的字符与栈顶的元素不匹配,则失败
            if (myStack.empty() || !isMatch(myStack.top(), s[i])) {
                printf("no");
                return 0;
            }
            myStack.pop();
        }
    }
    //最后判断栈中元素是否为空
    if (!myStack.empty()) {
        printf("no");
        return 0;
    }
    printf("yes");
    return 0;
}

计算表达式

输入:一个不含括号的表达式

输出:最终的值

处理:得出结果。

思路

以123+456-123/489*10为例

1.我们需要定义两个栈,运算符栈和操作数栈

2.当我们从左往右扫描运算符;当我们扫描到运算符之后不能立即计算他,我们得兼顾看后面的运算符,若后面的运算符优先级更高,我们得先算后面的(故我们可以使用一个栈来存储运算符)

例如上面先扫描到了+。我们先将+压栈{+},再扫描到后面的运算符是-,因为-的运算符并不比+高,所以这是+便可以计算了,即可以从栈中弹出,再将-压栈,此时的栈便变成了{-}

3.操作时机

压栈:栈为空或新的运算符的优先级更高

弹栈:新的运算符的优先级更低

代码:

代码过程:

numStack 表示数字栈

opStack 表示运算符栈

循环扫描字符串:

1.当扫描到的字符是数字字符,就加入到字符串 numStr中,而后当扫描到非数字字符,就可以收网了。(即将前面的 numStr转为double,并压入数字栈中)

2.当扫描的是非数字字符(可能为运算符也可能已经到达字符串末尾):

1)若!opStack.empty()&&priority[str[i]]<=priority[opStack.top()] 即运算符栈不为空且当前扫描到的运算符的优先级比栈顶的要低;我们要做的是:循环弹栈和计算(先弹出右操作数,再弹出左操作数,再将运算符弹出来,计算完成之后把结果再压入numStack 中)。

2)如果运算符栈为空或者前扫描到的运算符的优先级比栈顶的要高,则将这个运算符压栈。当然,如果是到了字符串的末尾,则直接打印numStack的元素(强制为整数)。并结束循环。

3.我们可以使用一个map结构来维持运算符的优先级。

#define  _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include<string>
#include<stack>
#include<map>
using namespace std;
​
int main() {
    //输入;一个不含括号的表达式
    char str[1024] = { 0 };
    //使用一个map结构来维持运算符的优先级
    map<char, int> priority = {
        {'\0',0}, {'+',1}, {'-',1},{'*',2}, {'/',2},
    };
​
    while(scanf("%s", str) != EOF) {
        stack<char>opStack;
        stack<double>numStack;
        //提取数值
        string numStr = "";
        //遍历字符串
        for (int i = 0;; i++) {
            if (str[i] >= '0' && str[i] <= '9') {
                numStr.push_back(str[i]);
            }
            else {
                //读到的不是0-9字符,意味着前面读到的但就是0-9字符
                double num = stod(numStr);
                numStr = "";
                //把数据识别出来后便压栈
                numStack.push(num);
                //什么时候弹栈 栈非空&&新op的优先级不高于栈顶的优先级
                //循环弹栈和计算
                while (!opStack.empty() && priority[str[i]] <= priority[opStack.top()]) {
                    //先弹出右操作数
                    double rhs = numStack.top();
                    numStack.pop();
                    //再弹出左操作数
                    double lhs = numStack.top();
                    numStack.pop();
                    //再将运算符弹出来
                    char curOp = opStack.top();
                    opStack.pop();
                    //计算
                    if (curOp == '+') {
                        numStack.push(lhs + rhs);
                    }
                    else if (curOp == '-') {
                        numStack.push(lhs - rhs);
                    }
                    else if (curOp == '*') {
                        numStack.push(lhs * rhs);
                    }
                    else if (curOp == '/') {
                        numStack.push(lhs / rhs);
                    }
                }
                //如果离开了这个循环,则说明栈悟空或新op的优先级高于栈顶
                if (str[i] == '\0') {
                    printf("%d\n", (int)numStack.top());
                    break;
                }
                else {
                    opStack.push(str[i]);
                }
​
            }
        }
​
    }
    return 0;
}

代码分析

1.上面我们将字符串转为数字,不适用int,而使用double,为什么?

==》虽然整数int的加法和减法无影响,但整数的乘法和除法会存在数据丢失的问题;所以我们将数字字符转成double;

2.上述代码将for循环中判断循环是否成立的语句放到循环体中,这样有什么好处?

==》我们可以在里面写一些循环结束前的处理;例如在上面的循环中,再退出循环前,我们需要打印出结果(形式为整数)。

 if (str[i] == '\0') {
        printf("%d\n", (int)numStack.top());
         break;
                }

2.我们来详细分析以下的代码:

char str[1024];
while (scanf("%s", str) != EOF) {
    // 处理字符串
}

1.scanf的行为 scanf("%s", str) 会读取输入中的字符串,直到遇到空白字符(如空格、换行符等)。 每次读取的字符串都会以 \0 结尾。 如果输入结束(例如文件结束或用户输入 Ctrl+D),scanf 会返回 EOF,循环结束。

2.字符数组最后的值:scanf("%s", str) 会在 str 的末尾自动添加 \0,这是 C 风格字符串的特性,只有用字符数组表示字符串时才会有这种情况。std::string 则不需要显式处理 \0。

3.如果我在最开始输入结束后,使用string s=str来接收,判断字符遍历接收的条件可以用:if(i==s.size()),如下所示:

 // 如果离开了这个循环,则说明栈空或新op的优先级高于栈顶
                if (i == s.size()) {
                    printf("%d\n", (int)numStack.top());
                    break;
                } else {
                    opStack.push(s[i]);
                }


http://www.kler.cn/a/579879.html

相关文章:

  • flutter的HTTP headers用法介绍
  • 单调栈、单调队列
  • 每天一道算法题【蓝桥杯】【四数之和】
  • 李沐《动手学深度学习》——14.9. 用于预训练BERT的数据集——wiki数据集问题以及存在的其他问题
  • 基于一致性哈希的分布式Top-K
  • 基于Flink SQL的实时指标多维分析模型
  • python办公自动化--数据可视化(pandas+matplotlib)--生成条形图和饼状图
  • Spring MVC 全面解析
  • 第六次CCF-CSP认证(含C++源码)
  • Elasticsearch 入门教学:从零开始掌握分布式搜索引擎
  • ESP32的IDF开发学习-移植lvgl并显示简单ui(以gc9a01为例)
  • 内网安全-横向移动PTH 哈希PTT 票据PTK 密匙Kerberos密码喷射
  • 蓝桥杯备考:离散化详解
  • 数据结构:有序表的插入
  • MongoD和关系型数据库相关概念的对应
  • PyTorch 张量数据类型定义和转换
  • 【Linux内核系列】:深入理解缓冲区
  • 在ubuntu 24 命令行 下,制作无人值守ubuntu-24.04.2-desktop 桌面版安装U盘
  • 《深入理解Linux:高效崩溃分析与实时栈回溯技巧》
  • 操作系统知识点25