1 题目描述
图1 题目描述
2 题目解读
这一题需要使用栈,完成中缀算术表达式的求值。另外,这一题要求算法具有拼数功能,拼数功能的实现,可以使用C语言库函数atof()函数来完成。
3 解法一:基于顺序栈的中缀算术表达式求值
顺序栈是使用顺序存储结构实现的栈,在栈数据结构中很重要。
3.1 解题思路
1、初始化OPTR栈和OPND栈,将表达式起始符"#"压入OPTR栈。
2、读取表达式,读入第一个字符ch,如果表达式没有读取完毕至"="或OPTR的栈顶元素不为"#"时,则循环执行以下操作。
(1)若ch不是运算符,则将从ch到下一个运算符之前的字符拼成双精度浮点数,压入OPND栈,读入下一字符ch。
(2)若ch是运算符,则根据OPTR的栈顶元素和ch的优先级比较结果,做不同的处理。
a.若小于,则将ch压入OPTR栈,读入下一字符ch;
b.若大于,则弹出OPTR栈顶的运算符,从OPND栈弹出两个数,进行相应运算,将结果压入OPND栈;
c.若等于,则OPTR的栈顶元素是"("且ch是")",这时弹出OPTR栈顶的"(",相当于括号匹配成功,然后读入下一字符ch。
3、OPND栈顶元素即表达式求值结果,返回此元素。
3.2 设计代码
cpp
#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//Status是函数返回值类型,其值是函数结果状态代码
#define MAXSIZE 100//顺序栈存储空间的初始分配量
typedef int Status;
typedef struct
{
char *base; //栈底指针
char *top; //栈顶指针
int stacksize;//栈可用的最大容量
}SqStack;
Status InitStack(SqStack &S);
Status Push(SqStack &S, char e);
Status Pop(SqStack &S, char &e);
char GetTop(SqStack S);
typedef struct
{
double *base; //栈底指针
double *top; //栈顶指针
int stacksize;//栈可用的最大容量
}SqStack2;
Status InitStack2(SqStack2 &S);
Status Push2(SqStack2 &S, double e);
Status Pop2(SqStack2 &S, double &e);
double GetTop2(SqStack2 S);
//表达式求值算法
double EvaluateExpression(char ch);
//算法调用的3个函数
bool In(char ch);
char Precede(char optr, char ch);
double Operate(double x, char theta, double y);
int main() {
char ch;
while (true) {
cin >> ch;
if ('=' == ch) {
break;
}
double res = EvaluateExpression(ch);
//保留小数点后面2位
cout << fixed << setprecision(2) << res << endl;
}
return 0;
}
double EvaluateExpression(char ch)
{
SqStack2 OPND; //操作数栈
SqStack OPTR; //运算符栈
InitStack2(OPND);//初始化OPND栈
InitStack(OPTR); //初始化OPTR栈
Push(OPTR, '#');
double x;
while (ch != '=' || GetTop(OPTR) != '#')
{
if (!In(ch)) {//如果ch不是运算符
char str[100] = { 0 };
int i = 0;
while (!In(ch)) {//拼数时ch可能是'='
str[i++] = ch;
cin >> ch;
}
x = atof(str);
Push2(OPND, x);
}
else {
//比较OPTR的栈顶元素和ch的优先级
switch (Precede(GetTop(OPTR), ch))
{
case '<':
Push(OPTR, ch); cin >> ch;
break;
case '>':
char theta;
double a, b;
Pop(OPTR, theta);
Pop2(OPND, b);
Pop2(OPND, a);
Push2(OPND, Operate(a, theta, b));
break;
case '=':
char x;
Pop(OPTR, x); cin >> ch;
break;
}
}
}
return GetTop2(OPND);
}
//判定读入的字符ch是否为运算符的函数
bool In(char ch)
{
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
ch == '(' || ch == ')' || ch == '=') {
return true;
}
return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
char Precede(char optr, char ch)
{
//规则(1)先乘除,后加减
if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {
return '<';
}
else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {
return '>';
}
//规则(2)从左算到右
if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||
((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {
return '>';
}
//规则(3)先括号内,后括号外
//optr不会出现右括号
if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {
if (ch == '(') {
return '<';
}
else if (ch == ')' || ch == '=') {
return '>';
}
}
else if (optr == '(') {
if (ch == ')') {
return '=';
}
else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {
return '<';
}
}
//optr中只有'#'的情况
if (optr == '#' &&
(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {
//ch肯定不是'='
return '<';
}
}
//进行二元运算的函数
double Operate(double x, char theta, double y)
{
double z;
switch (theta)
{
case '+':
z = x + y;
break;
case '-':
z = x - y;
break;
case '*':
z = x * y;
break;
case '/':
z = x / y;
break;
default:
z = 0;
}
return z;
}
Status InitStack(SqStack &S)
{
S.base = new char[MAXSIZE];
if (!S.base) exit(OVERFLOW);
S.top = S.base;
S.stacksize = MAXSIZE;
return OK;
}
Status Push(SqStack &S, char e)
{
if (S.top - S.base == S.stacksize) return ERROR;
*S.top++ = e;
return OK;
}
Status Pop(SqStack &S, char &e)
{
if (S.top == S.base) return ERROR;
e = *--S.top;
return OK;
}
char GetTop(SqStack S)
{
if (S.top != S.base) {
return *(S.top - 1);
}
}
Status InitStack2(SqStack2 &S)
{
S.base = new double[MAXSIZE];
if (!S.base) exit(OVERFLOW);
S.top = S.base;
S.stacksize = MAXSIZE;
return OK;
}
Status Push2(SqStack2 &S, double e)
{
if (S.top - S.base == S.stacksize) return ERROR;
*S.top++ = e;
return OK;
}
Status Pop2(SqStack2 &S, double &e)
{
if (S.top == S.base) return ERROR;
e = *--S.top;
return OK;
}
double GetTop2(SqStack2 S)
{
if (S.top != S.base) {
return *(S.top - 1);
}
}
3.3 执行结果
图2 基于顺序栈的中缀算术表达式求值代码执行结果
4 解法二:基于链栈的中缀算术表达式求值
链栈是使用链式存储结构实现的栈,是栈的另一种实现方式。
4.1 解题思路
基于链栈的解题思路与基于顺序栈的解题思路一样,不同的只是栈的实现方式。
4.2 设计代码
cpp
#include <iostream>
#include <iomanip>
using namespace std;
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//Status是函数返回值类型,其值是函数结果状态代码
#define MAXSIZE 100//顺序栈存储空间的初始分配量
typedef int Status;
typedef struct StackNode
{
char data;
struct StackNode *next;
}StackNode, *LinkStack;
Status InitStack(LinkStack &S);
Status Push(LinkStack &S, char e);
Status Pop(LinkStack &S, char &e);
char GetTop(LinkStack S);
typedef struct StackNode2
{
double data;
struct StackNode2 *next;
}StackNode2, *LinkStack2;
Status InitStack2(LinkStack2 &S);
Status Push2(LinkStack2 &S, double e);
Status Pop2(LinkStack2 &S, double &e);
double GetTop2(LinkStack2 S);
//表达式求值算法
double EvaluateExpression(char ch);
//算法调用的3个函数
bool In(char ch);
char Precede(char optr, char ch);
double Operate(double x, char theta, double y);
int main() {
char ch;
while (true) {
cin >> ch;
if ('=' == ch) {
break;
}
double res = EvaluateExpression(ch);
//保留小数点后面2位
cout << fixed << setprecision(2) << res << endl;
}
return 0;
}
double EvaluateExpression(char ch)
{
LinkStack2 OPND; //操作数栈
LinkStack OPTR; //运算符栈
InitStack2(OPND);//初始化OPND栈
InitStack(OPTR); //初始化OPTR栈
Push(OPTR, '#');
double x;
while (ch != '=' || GetTop(OPTR) != '#')
{
if (!In(ch)) {//如果ch不是运算符
char str[100] = { 0 };
int i = 0;
while (!In(ch)) {//拼数时ch可能是'='
str[i++] = ch;
cin >> ch;
}
x = atof(str);
Push2(OPND, x);
}
else {
//比较OPTR的栈顶元素和ch的优先级
switch (Precede(GetTop(OPTR), ch))
{
case '<':
Push(OPTR, ch); cin >> ch;
break;
case '>':
char theta;
double a, b;
Pop(OPTR, theta);
Pop2(OPND, b);
Pop2(OPND, a);
Push2(OPND, Operate(a, theta, b));
break;
case '=':
char x;
Pop(OPTR, x); cin >> ch;
break;
}
}
}
return GetTop2(OPND);
}
//判定读入的字符ch是否为运算符的函数
bool In(char ch)
{
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
ch == '(' || ch == ')' || ch == '=') {
return true;
}
return false;
}
//判定运算符栈的栈顶元素与读入的运算符之间优先关系的函数
char Precede(char optr, char ch)
{
//规则(1)先乘除,后加减
if ((optr == '+' || optr == '-') && (ch == '*' || ch == '/')) {
return '<';
}
else if ((optr == '*' || optr == '/') && (ch == '+' || ch == '-')) {
return '>';
}
//规则(2)从左算到右
if (((optr == '+' || optr == '-') && (ch == '+' || ch == '-')) ||
((optr == '*' || optr == '/') && (ch == '*' || ch == '/'))) {
return '>';
}
//规则(3)先括号内,后括号外
//optr不会出现右括号
if (optr == '+' || optr == '-' || optr == '*' || optr == '/') {
if (ch == '(') {
return '<';
}
else if (ch == ')' || ch == '=') {
return '>';
}
}
else if (optr == '(') {
if (ch == ')') {
return '=';
}
else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(') {
return '<';
}
}
//optr中只有'#'的情况
if (optr == '#' &&
(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(')) {
//ch肯定不是'='
return '<';
}
}
//进行二元运算的函数
double Operate(double x, char theta, double y)
{
double z;
switch (theta)
{
case '+':
z = x + y;
break;
case '-':
z = x - y;
break;
case '*':
z = x * y;
break;
case '/':
z = x / y;
break;
default:
z = 0;
}
return z;
}
Status InitStack(LinkStack &S)
{
//构造一个空栈S,栈顶指针置空
S = NULL;
return OK;
}
Status Push(LinkStack &S, char e)
{
//在栈顶插入元素e
StackNode *p = new StackNode;
p->data = e;
p->next = S;
S = p;
return OK;
}
Status Pop(LinkStack &S, char &e)
{
//删除S的栈顶元素,用e返回其值
if (S == NULL) return ERROR;
e = S->data;
StackNode *p = S;
S = S->next;
delete p;
return OK;
}
char GetTop(LinkStack S)
{
//返回S的栈顶元素,不修改栈顶指针
if (S != NULL) { //栈非空
return S->data; //返回栈顶元素的值,栈顶指针不变
}
}
Status InitStack2(LinkStack2 &S)
{
//构造一个空栈S,栈顶指针置空
S = NULL;
return OK;
}
Status Push2(LinkStack2 &S, double e)
{
//在栈顶插入元素e
StackNode2 *p = new StackNode2;
p->data = e;
p->next = S;
S = p;
return OK;
}
Status Pop2(LinkStack2 &S, double &e)
{
//删除S的栈顶元素,用e返回其值
if (S == NULL) return ERROR;
e = S->data;
StackNode2 *p = S;
S = S->next;
delete p;
return OK;
}
double GetTop2(LinkStack2 S)
{
//返回S的栈顶元素,不修改栈顶指针
if (S != NULL) { //栈非空
return S->data;//返回栈顶元素的值,栈顶指针不变
}
}
4.3 执行结果
图3 基于链栈的中缀算术表达式求值代码执行结果
5 解题心得
- 栈这一数据结构,如果使用自己实现的,则代码较多,工作量较大。
- 表达式求值算法,可以完成中缀算术表达式的求值。
- 代码的完善是一项费时的工作。