【栈】实现表达式求值

思路 && 理解 && 注意

给定一串表达式,字符串类型,依次遍历从头开始遍历每一个位置的内容。

第一个数字,第一个运算符先直接往栈里面push(两个不同的栈)
接着走,遇到数push进来,接着走,遇到运算符,和前面那个已经push进栈的运算符进行优先级比较,如果当前运算符优先级大,那就接着push进来,反之,pop出栈,运算前面的式子之和(之后判断运算符栈中是否还有内容,并且当前运算符的优先级是否小于等于已有的运算符,小于等于就接着运算前面的表达式,完成push当前运算符,反之继续往下遍历push…pop…),直到最后一个元素。

注意;

一直发生变化的是rdata-右操作数,所以每次压完运算符找新的右操作数都会将他置空,准备重新赋值。

没有添加括号优先级运算。

expression.h

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#pragma once
#include<iostream>
using namespace std;


#define MAX_SIZE 128

typedef struct _Postion//地图中点的坐标,这个栈中存的元素就是点的坐标
{
int _x;
int _y;
}Postion;

typedef int DataType;


//栈的结构体
typedef struct _Stack
{
DataType* top;
DataType* base;
}Stack;

//栈的初始化
bool initStack(Stack& S)
{
S.base = new DataType[MAX_SIZE];
if (!S.base)
{
return false;
}
S.top = S.base;
return true;
}

//入栈
bool pushStack(Stack& S, DataType data)
{
if (!S.base)
{
return false;
}
if (S.top - S.base == MAX_SIZE)
{
return false;
}

*(S.top) = data;
S.top++;
return true;
}

//出栈
bool popStack(Stack& S, DataType& e)
{
if (S.top == S.base)
{
return false;
}
e = *(--S.top);
return true;
}

//返回栈顶元素
DataType* getTop(Stack& S)
{
if (S.top - S.base == 0)
{
return NULL;
}
//注意何时自增何时不自增
return S.top - 1;//返回栈顶元素的指针
}

//返回栈中元素个数
int getSize(Stack& S)
{
return S.top - S.base;
}

//判断栈是否为空
bool isEmpty(Stack& S)
{
if (S.top == S.base)
{
return true;
}
else
{
return false;
}
}

//销毁栈
void destoryStack(Stack& S)
{
if (S.base)
{
delete[] S.base;
S.top = S.base = NULL;
}
}

experssion.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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include"expression.h"
#include<iostream>
using namespace std;

//比较 lhs 的优先级是否高于 rhs,rhs 表示栈顶的符号

bool isLarger(const int &lhs, const int &rhs)
{
if ((rhs == '+' || rhs == '-') && (lhs == '*' || lhs == '/'))
{
return true;
}
return false;
}

//计算左右操作数+运算符 (对运算符求值)
int operate(int left, int right, int op)
{
int result = 0;
switch (op)
{
case '+':
result = left + right;
break;
case '-':
result = left - right;
break;
case '*':
result = left * right;
break;
case '/':
result = left / right;
break;
default:
break;
}
return result;
}

//运算主体
int calculate(string input)
{
Stack data_stack;//操作数堆栈
Stack opt_stack;//运算符堆栈

int status = 0;//0接收左操作数,1接收操作符,2,接收右操作数

//左右操作数
//一直在发生变化的是右操作符
int ldata = 0;
int rdata = 0;

char last_opt = '\0';

//初始化堆栈
initStack(data_stack);
initStack(opt_stack);

//从第一个开始遍历
for (int i = 0; i < input.length(); i++)
{
if (isspace(input[i]))//跳过空白符
{
continue;
}

//不是空白,第一次到这里,默认是status = 0是左操作数
switch (status)
{
//isdigit-判断是否是十进制数字
case 0:
//得到做操作数左操作数
/*
左操作数是如何得到的
遍历字符串,第一个得到的肯定是左操作数,但我们不知道它是几位数。默认ldata为0
其实就是——这个数是几位,这个if()条件就能进来几次
累加在ldata中,得到左操作数
*/
if (isdigit(input[i]))
{
ldata *= 10;
ldata += input[i] - '0';//求出该位上这个数是几
}

//什么时候执行到这里?
//第一个数字得到之后,也就是得到了ldata之后
else
{

pushStack(data_stack, ldata);//左操作数进栈

//现在input[i]的位置是运算符
//因为结束case结束之后,出来for循环还得++,这样就错过这个运算符了
//为了保证到case 1的语句中此时的input[i]是运算符,所以要字先--
i--;

status = 1;//操作数确定了,下一个就该运算符了。
}
break;



case 1://遇到操作符
if (input[i] == '+' || input[i] == '-' || input[i] == '*' || input[i] == '/')
{
if (isEmpty(opt_stack))//第一个运算符暂时不做任何处理,先入栈保存
{
pushStack(opt_stack, input[i]);//第一个操作符进栈
//运算符进栈存的是对应符号的ASCII码

status = 2;//状态标记为2 下一个为右操作数
}
else//不是第一个运算符,那么就将这个与之前的做优先级比较,如果这个优先级高,那就先算这个
{


//当前运算符高于前一个运算符

//当前input[i]运算符 栈里面的存的第一个运算符
if (isLarger(input[i], *getTop(opt_stack)))//如果当前运算符的优先级高于前一个
{
//压进栈
pushStack(opt_stack, input[i]);//操作符入栈

status = 2;//下一个是右操作数
rdata = 0;//将右操作数置空
}
else//当前运算符的优先级小于(等于)前一个(栈顶)运算符。则计算前一个运算符的值
{
int right = 0;
int left = 0;
int opt = 0;

do
{
//拿到操作符 和 前面两个左右操作数
//先取到右边的,在取左边的(倒着拿出来)
//运算的时候注意参数传递顺序
popStack(data_stack, right);
popStack(data_stack, left);
popStack(opt_stack, opt);

int result = operate(left, right, opt);
pushStack(data_stack, result);//得到一部分的结果压进栈
} while (!isEmpty(opt_stack) && !isLarger(input[i],*getTop(opt_stack)));//自动再往前判断,是否可以对前面的表达式进行运算
//运算符栈不为空 并且当前运算符优先级小于等于栈顶运算符(前面的)那么就能一并进行运算

//将当前input[i]运算符压入栈
pushStack(opt_stack, input[i]);

status = 2;//去右操作数
rdata = 0;//置空
}
}
}
else if (input[i] == '=')//到达结尾
{
int opt = 0;
int result = 0;
do
{

popStack(data_stack, rdata);
popStack(data_stack, ldata);
popStack(opt_stack, opt);

result = operate(ldata, rdata, opt);
pushStack(data_stack, result);
} while (!isEmpty(opt_stack));

//返回得到最后结果
return result;
}
else
{
cerr << "运算符输入错误" << endl;
}
break;

case 2://右操作数
if (isdigit(input[i]))//同上求左操作数,求出rdata右操作数
{
rdata *= 10;
rdata += input[i] - '0';
}
else
{
pushStack(data_stack, rdata);//右操作数入栈
i--;
status = 1;
}
break;
}
}
return -1;
}


int main(void)
{
string str = "12+3*6/3+4*5=";
cout << calculate(str) << endl;//38
return 0;
}