【C++复习】栈-下篇
Table of Contents

大家好,这里是不会写开场白的Yinph。

今天我们先来复习一下中缀表达式、前缀表达式和后缀表达式,以及如何用栈来实现它们之间的运算。

一、中缀表达式 Link to 一、中缀表达式

‌中缀表达式‌是一种算术或逻辑公式的表示方法,其中操作符位于操作数的中间。这种表示方法符合人们的日常书写习惯,因此被广泛使用。

然而,对于计算机来说,处理中缀表达式相对复杂,因为计算机更擅长处理前缀或后缀表达式。中缀表达式的主要特点是将运算符放在操作数的中间,例如“3 + 4”、“114514*1919810”都是中缀表达式。

二、前缀表达式 Link to 二、前缀表达式

(1)什么是前缀表达式? Link to (1)什么是前缀表达式?

前缀表达式,也称为波兰表达式,是一种算术或逻辑公式的表示方法,其中操作符位于操作数之前。

前缀表达式的主要特点是去掉了中缀表达式中用于明确运算顺序的括号,通过运算符的位置直接表明运算的优先级和顺序。这种表示方式使得表达式的计算过程更加简洁和直观(对计算机来说)。

前缀表达式在计算机科学中主要用于算法设计和实现,特别是在需要高效处理大量数学运算的场景中。例如,在编译器的设计中,前缀表达式可以帮助简化表达式的解析和计算过程。

例如,中缀表达式3 + 4 * 5转换成前缀表达式为+ 3 * 4 5

(2)中缀表达式转前缀表达式 Link to (2)中缀表达式转前缀表达式

中缀表达式转前缀表达式有很多种方法,其中一种简单的方法就是添加括号法。

主要分成三个步骤进行:

  • 1.根据运算符的优先级对中缀表达式加括号,将优先级高的运算符放在括号内

  • 2.将运算符移到对应的括号前面

  • 3.去掉所有括号

例如,中缀表达式82 + 3 * (20 - 8) / 2转换成前缀表达式为+ 82 * 3 / - 20 8 2

文字表达大家可能不太理解,我这里用一张图片来解释:

转前缀表达式

(3)计算过程 Link to (3)计算过程

前缀表达式在计算过程中主要分成四步来进行:

1.从右到左遍历表达式;

2.遇到数字就进栈;

3.遇到符号,就将栈顶元素出栈作为第一操作数,接着将新的栈顶元素出栈作为第二操作数,进行运算,然后将结果进栈;

4.重复以上操作直到表达式遍历结束,栈中的元素就是结果。

对于第一操作数和第二操作数是什么这个问题,一张图片就能解释了:

第一操作数和第二操作数

计算过程我来详细解释下:

我们以前缀表达式- 1 * + 3 4 5为例:

首先,我们从右往左,先将数字5入栈,接着将数字4入栈,最后将数字3入栈。

我们遇到操作符+,出栈,将栈顶元素3出栈作为第一操作数,接着将新的栈顶元素4出栈作为第二操作数,3 + 4 = 7,然后将结果7进栈。

计算过程

接着,我们将符号*出栈,将栈顶元素7出栈作为第一操作数,接着将新的栈顶元素5出栈作为第二操作数,7 * 5 = 35,与遇到的数字1同时进栈。

计算过程

最后,我们遇到操作符-,将目前栈顶元素1出栈作为第一操作数,接着将新的栈顶元素35出栈作为第二操作数,1 - 35 = -34,然后将结果-34进栈。

计算过程

最终,我们得到答案-34.

三、后缀表达式 Link to 三、后缀表达式

(1)什么是后缀表达式? Link to (1)什么是后缀表达式?

后缀表达式,也称为逆波兰表达式,是一种算术或逻辑公式的表示方法,其中操作符位于操作数之后。

它简化了表达式的解析和计算过程,去掉了中缀表达式中用于明确运算顺序的括号,通过运算符的位置直接表明运算的优先级和顺序。这种表示方式也使得表达式的计算过程更加简洁和直观。

同一个例子:中缀表达式中3 + 4 * 5转换成后缀表达式为3 4 5 * +

(2)中缀表达式转后缀表达式 Link to (2)中缀表达式转后缀表达式

同样分成三个步骤进行:

  • 1.根据运算符的优先级对中缀表达式加括号,将优先级高的运算符放在括号内

  • 2.将运算符移到对应的括号后面

  • 3.去掉所有括号

同一个例子,中缀表达式82 + 3 * (20 - 8) / 2转换成后缀表达式为82 3 20 8 - * 2 / +

转后缀表达式

(3)计算过程 Link to (3)计算过程

当然,后缀表达式在计算过程中也主要分成四步来进行:

1.从左到右遍历表达式;

2.遇到数字就进栈;

3.遇到符号,就将栈顶元素出栈作为第二操作数,接着将新的栈顶元素出栈作为第一操作数,进行运算,然后将结果进栈;

4.重复以上操作直到表达式遍历结束,栈中的元素就是结果。

大家可能还不是很理解,我再来详细解释下:

我们以后缀表达式82 3 20 8 - * 2 / +为例:

首先,我们从左到右,先将数字82入栈,接着将数字3入栈,然后将数字20入栈,最后将数字8入栈。

我们遇到操作符-,出栈,将栈顶元素8出栈作为第二操作数,接着将新的栈顶元素20出栈作为第一操作数,20 - 8 = 12,然后将结果12进栈。

计算过程

接着,我们将符号*出栈,将栈顶元素12出栈作为第二操作数,接着将新的栈顶元素3出栈作为第一操作数,3 * 12 = 36,然后将结果36进栈。

计算过程

然后,我们将数字2入栈,遇到符号/出栈,将栈顶元素2出栈作为第二操作数,接着将新的栈顶元素36出栈作为第一操作数,36 / 2 = 18,然后将结果18进栈。

计算过程

最后,我们遇到操作符+,将目前栈顶元素18出栈作为第二操作数,接着将新的栈顶元素82出栈作为第一操作数,82 + 18 = 100,然后将结果100进栈。

计算过程

最终,我们得到答案100.

现在,我们已经了解了前、中、后缀表达式,以及它们之间的计算。接下来,我们就用代码来实现它们之间的运算。

四、代码解决前、后缀表达式运算 Link to 四、代码解决前、后缀表达式运算

声明一下,下文给出的代码仅演示加、减、乘、除四个常见操作符。有需要可自行添加。

(1)数组模拟 Link to (1)数组模拟

首先,我们可以用数组来模拟栈,来实现前缀表达式和后缀表达式的运算。缺点就是,数组模拟栈的效率较低,且代码较多,不好理解。

代码……开写!

转前缀表达式 Link to 转前缀表达式

CPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int st[n+1]; // int数组模拟栈
int TOP = 0; // 栈顶指针初始化(归0)
void push(int x)//入栈
{	
    TOP++;	
    st[TOP] = x;	
}
void pop( ) //出栈
{	
    TOP--;	
}
int top( ) // 获取栈顶元素	
{	
    return st[TOP];	
}
int main()
{
    string a;
    getline(cin, a);
    for (int i = a.length() - 1; i >= 0; i--)
    {
        // 初始化sum和idx来构建数字
        int sum = 0, idx = 0;
        // 当当前字符是数字时,构建数字  
        while (a[i] >= '0' && a[i] <= '9')
        {
            sum = sum + (a[i] - '0') * pow(10, idx);
            idx++;
            i--; // 递减i以继续检查前一个字符  
        }
        if (sum != 0) // 如果遇到符号,则将其推入栈中  
        {
            push(sum);
        }
        // 如果当前字符是运算符,则处理运算符  
        if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/')
        {
            int num1 = top();
            pop(); // 弹出栈顶元素
            int num2 = top();
            pop();
            // 加、减、乘、除运算:
            if (a[i] == '+') push(num2 + num1);
            else if (a[i] == '-')  push(num2 - num1);
            else if (a[i] == '*')  push(num2 * num1);
            else if (a[i] == '/')  push(num2 / num1);
        }
    }
    cout << top(); // 输出结果
    return 0;
}

转后缀表达式 Link to 转后缀表达式

CPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int st[n+1]; // int数组模拟栈
int TOP = 0; // 栈顶指针初始化(归0)
void push(int x)//入栈
{	
    TOP++;	
    st[TOP] = x;	
}
void pop( ) //出栈
{	
    TOP--;	
}
int top( ) // 获取栈顶元素	
{	
    return st[TOP];	
}
int main()
{
    string a;
    getline(cin, a);
    for (int i = 0; i < a.length() - 1; i++)
    {
        // 初始化sum来构建数字
        int sum = 0;
        // 当当前字符是数字时,构建数字
        while (a[i] >= '0' && a[i] <= '9')
        {
            sum = sum * 10 + (a[i] - '0');
            i++;
        }
        if (sum != 0)
        {
            push(sum);
        }
        if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/')
        {
            int num2 = top();
            pop(); 
            int num1 = top();
            pop();
            if (a[i] == '+') push(num1 + num2);
            else if (a[i] == '-')  push(num1 - num2);
            else if (a[i] == '*')  push(num1 * num2);
            else if (a[i] == '/')  push(num1 / num2);
        }
    }
    cout << top(); // 输出结果
    return 0;
}

(2)标准模板库(STL)模拟 Link to (2)标准模板库(STL)模拟

上篇文章,我们认识了标准模板库(STL),在栈中使用代码更加方便。那么,我们今天就来用STL来实现一下前缀表达式和后缀表达式的运算。

主要核心不变,只是相关函数更改而已。我还是把代码展示一下吧:

转前缀表达式 Link to 转前缀表达式

CPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <string>
#include <stack>
#include <cmath>
using namespace std;

int main()
{
    stack <int> s;
    string a;
    getline(cin, a);
    for (int i = a.length() - 1; i >= 0; i--)
    {
        // 初始化sum和idx来构建数字
        int sum = 0, idx = 0;
        // 当当前字符是数字时,构建数字  
        while (a[i] >= '0' && a[i] <= '9')
        {
            sum = sum + (a[i] - '0') * pow(10, idx);
            idx++;
            i--; // 递减i以继续检查前一个字符  
        }
        if (sum != 0) // 如果遇到符号,则将其推入栈中  
        {
            s.push(sum);
        }
        // 如果当前字符是运算符,则处理运算符  
        if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/')
        {
            int num1 = s.top();
            s.pop(); // 弹出栈顶元素
            int num2 = s.top();
            s.pop();
            // 加、减、乘、除运算:
            if (a[i] == '+') s.push(num2 + num1);
            else if (a[i] == '-')  s.push(num2 - num1);
            else if (a[i] == '*')  s.push(num2 * num1);
            else if (a[i] == '/')  s.push(num2 / num1);
        }
    }
    cout << s.top(); // 输出结果
    return 0;
}

转后缀表达式 Link to 转后缀表达式

CPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <string>
#include <stack>
#include <cmath>
using namespace std;

int main()
{
    stack <int> s;
    string a;
    getline(cin, a);
    for (int i = 0; i < a.length() - 1; i++)
    {
        // 初始化sum来构建数字
        int sum = 0;
        // 当当前字符是数字时,构建数字
        while (a[i] >= '0' && a[i] <= '9')
        {
            sum = sum * 10 + (a[i] - '0');
            i++;
        }
        if (sum != 0)
        {
            push(sum);
        }
        if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/')
        {
            int num2 = top();
            pop(); 
            int num1 = top();
            pop();
            if (a[i] == '+') push(num1 + num2);
            else if (a[i] == '-')  push(num1 - num2);
            else if (a[i] == '*')  push(num1 * num2);
            else if (a[i] == '/')  push(num1 / num2);
        }
    }
    cout << top(); // 输出结果
    return 0;
}

五、总结一下 Link to 五、总结一下

在本文中,我们主要学习了前缀表达式、后缀表达式与如何使用栈来解决运算问题。

希望这篇文章能帮助你们更好地理解栈,如果你有任何问题,欢迎在评论区留言。

OK,以上就是今天要讲的内容。大家喜欢就点个赞吧,我会尽快更新!ヾ(•ω•`)o!

Thanks for reading!

【C++复习】栈-下篇

Thu Aug 22 2024
3434 字 · 19 分钟