【表达式求值】
创始人
2024-01-19 14:50:13

目录:

  • 前言
  • 一、有效的括号
    • (一)题目分析
    • (二)整体代码
  • 二、表达式求值
    • (一)题目分析
      • 1.栈的基本操作:
      • 2. 大体思路:
      • 3.具体计算过程:
    • (二)整体代码
  • 总结

前言

在这里插入图片描述
朋友们大家好,前段时间熊猫我学习了栈的相关知识,通过做题发现栈可以解决一些很有价值的事情,比如:表达式求值。
下面我会通过LeetCode上《有效的括号》这个题进行引入,讲解表达式求值的实现,希望能为大家带来帮助。


一、有效的括号

点击跳转LeetCode:有效的括号

题目描述:
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
在这里插入图片描述

(一)题目分析

本题要求我们判断括号是否有效,那么我们需要知道什么形式的括号才是有效的?
1.括号的总数量一定是偶数个,并且左右括号各占一半;
2.左括号必须和与之对应的右括号相结合,既 ‘(’ 必须和 ‘)’ 对应,‘[’ 和 ‘]’ 以及 ‘{’ 和 ‘}’ 亦然;
3.必须先有左括号再有右括号。
通过上面的分析我们可以知道,遇到左括号就入栈,出现右括号就和栈区最后一个左括号进行匹配,匹配上了就出栈,否则就是无效的括号,并且最后栈区是空的。

括号一定是成对出现的

	int sz = strlen(s);if (sz % 2 != 0) return false;

遇到左括号就入栈,遇到右括号就出栈

char* arr = (char*)malloc(sz);int a = 0; // 记录栈区字符的个数for (int i = 0; i < sz; i++){switch (s[i]){case '(':case '[':case '{':arr[a++] = s[i];break;case ')':if (a > 0 && arr[--a] == '(')break;elsereturn false;case ']':if (a > 0 && arr[--a] == '[')break;elsereturn false;case '}':if (a > 0 && arr[--a] == '{')break;elsereturn false;}}

最后我们还需要判断栈区是否为空,防止左括号比右括号多

 if(a==0)return true;elsereturn false;

(二)整体代码

示例:

bool isValid(char* s) {int sz = strlen(s);if (sz % 2 != 0)return false;char* arr = (char*)malloc(sz);int a = 0; // 记录栈区字符的个数for (int i = 0; i < sz; i++){switch (s[i]){case '(':case '[':case '{':arr[a++] = s[i];break;case ')':if (a > 0 && arr[--a] == '(')break;elsereturn false;case ']':if (a > 0 && arr[--a] == '[')break;elsereturn false;case '}':if (a > 0 && arr[--a] == '{')break;elsereturn false;}}if(a==0)return true;elsereturn false;
}

运行实例:
在这里插入图片描述


二、表达式求值

上一个例题我们写的十分简陋,虽然说是用到了栈,不过只是用到了它的思想而没有具体实现出来,下面一个例题熊猫我会直接先把栈区的操作实现出来,让大家真真正正滴了解一下栈的操作(防止大家觉得熊猫我不够专业)。

题目描述:
以字符序列的形式从终端输入语法正确的、不含变量的整数表达式,
我们需要实现对算术四则混合运算表达式的求值。
测试数据:
3*(7-1);1+2+3+4;88-15;(20+2)(6/2);6+2*(3+6)。

(一)题目分析

题目要求我们实现对算数四则混合运算表达式的求值,这里我们首先需要注意两点:
1.小括号的优先级最高,我们需要先进行括号里的表达式运算
2.乘法和除法的优先级高于加法和减法

1.栈的基本操作:

#define MALLOC(ty,num) (ty*)malloc(sizeof(ty)*(num))
#define REALLOC(block,ty,num) (ty*)realloc(block,sizeof(ty)*(num));
#define OK 1
#define ERROR 0
#define INITNUM 5	//初始化顺序表长度
#define ADDNUM 3	//之后每次增加的长度
typedef double selemtype;typedef struct SQStack
{selemtype* data;int length;int maxnum;
}SQS;void Init(SQS* p)  栈的初始化
{ assert(p);p->data = MALLOC(selemtype, INITNUM);if (!p->data){return;}p->length = 0;//顺序栈下一个元素的下标p->maxnum = INITNUM;
}void Destroy(SQS* p)  // 栈的销毁
{assert(p);free(p->data);p->length = 0;p->maxnum = 0;
}static int Judge(SQS* p)  // 判断栈区是否已满并扩容
{assert(p);if (p->length == p->maxnum)//满了{selemtype* newblock = REALLOC(p->data, selemtype, p->length + ADDNUM);if (newblock == NULL){return ERROR;}p->data = newblock;p->maxnum = p->length + ADDNUM;}return OK;
}void Push(SQS* p, selemtype con)    //  入栈
{assert(p);Judge(p);p->data[p->length] = con;p->length++;
}void Pop(SQS* p)   //  出栈
{assert(p);p->length--;
}int JudgeOverflow(SQS* p)   //  判断栈区是否为空
{assert(p);if (p->length == 0){return ERROR;}else{return OK;}
}selemtype GetTop(SQS* p)    //   获取栈顶数据
{assert(p);return p->data[p->length - 1];
}

2. 大体思路:

由于小括号运算优先级最高,所以我们需要先计算括号内再计算括号外;
我们这里可以先判断小括号是否存在,如果没有遇到括号、或者遇到了左括号我们就继续进栈,直到遇到了右括号我们再出栈,
当然,出栈也有限制:
1.出到了左括号就停止出栈,这时一个括号内的表达式就计算结束了
2.如果栈空了就说明这整个表达式就计算结束了,此时我们就已经功德圆满啦(bushi)。

selemtype Expression(char* str)
{SQS s;Init(&s);double con = 0; // 表达式结果char* p = NULL; // int i = 0;for (i = 0; str[i]; ) // 遍历字符串{if (isdigit(str[i])) // 判断是数值{//  本函数是将字符串转为double类型数据,并且返回转换数据结束时后一个位置的地址,我们将它存放在指针p中//这里不可以直接 push(str[i]);//是为了防止参与运算的数值中有小数、以及不止一位数的数据selemtype val = strtod(&str[i], &p); Push(&s, val);i = p - str; // 更新下标}else  // 符号{if (str[i] != ')'){Push(&s, str[i]);}else     // 如果遇到右括号我们就讲括号内的表达式进行一次求值{con = Evaluation(&s);Push(&s, con);   //  求得的值要继续入栈,最后需要再整体进行一次运算}i++; // 更新下标}}con = Evaluation(&s);    //  整体结果运算Destroy(&s);return con;
}

经过上面的理想操作,我们就可以得到想要的结果,
为了便于下面运算函数的实现,我们大家可以通过下面的图解来大致了解操作过程。
在这里插入图片描述

3.具体计算过程:

这里我们不需要考虑括号,主要考虑的是优先级的问题(还有一些特殊情况)
下面我们先来画图分析一下:
在这里插入图片描述

selemtype Evaluation(SQS* ps)
{selemtype con = 0;selemtype val1 = GetTop(ps); // 数值Pop(ps);selemtype val2 = 0;while (JudgeOverflow(ps)) // 栈为空就退出{switch ((char)GetTop(ps)){case'+':{Pop(ps); // 将 '+' 出栈val2 = GetTop(ps);Pop(ps);con += val1;val1 = val2;}break;case '-':{Pop(ps);if (!JudgeOverflow(ps))  //  特殊1.这个地方是要避免第一个数是负数{con -= val1;return con;}val2 = GetTop(ps);Pop(ps);con -= val1;val1 = val2;}break;case '*':{Pop(ps);val1 *= GetTop(ps);Pop(ps);}break;case '/':{Pop(ps);val2 = GetTop(ps);Pop(ps);if (GetTop(ps) == '/')  //  特殊2:出栈是后进先出的,因此如果出现连除情况需要特殊处理val1 = val1 * val2;elseval1 = val2 / val1;}break;case '(':Pop(ps);goto end;}}end:con += val1;return con;}

两种特殊情况:
在这里插入图片描述

(二)整体代码

示例:

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include#define MALLOC(ty,num) (ty*)malloc(sizeof(ty)*(num))
#define REALLOC(block,ty,num) (ty*)realloc(block,sizeof(ty)*(num));
#define OK 1
#define ERROR 0
#define INITNUM 5	//初始化顺序表长度
#define ADDNUM 3	//之后每次增加的长度
typedef double selemtype;typedef struct SQStack
{selemtype* data;int length;int maxnum;
}SQS;void Init(SQS* p)
{assert(p);p->data = MALLOC(selemtype, INITNUM);if (!p->data){return;}p->length = 0;//顺序栈下一个元素的下标p->maxnum = INITNUM;
}void Destroy(SQS* p)
{assert(p);free(p->data);p->length = 0;p->maxnum = 0;
}static int Judge(SQS* p)
{assert(p);if (p->length == p->maxnum)//满了{selemtype* newblock = REALLOC(p->data, selemtype, p->length + ADDNUM);if (newblock == NULL){return ERROR;}p->data = newblock;p->maxnum = p->length + ADDNUM;}return OK;
}//入栈
void Push(SQS* p, selemtype con)
{assert(p);Judge(p);p->data[p->length] = con;p->length++;}void Pop(SQS* p)
{assert(p);p->length--;
}int JudgeOverflow(SQS* p)
{assert(p);if (p->length == 0){return ERROR;}else{return OK;}
}selemtype GetTop(SQS* p)
{assert(p);return p->data[p->length - 1];
}selemtype Evaluation(SQS* ps)
{selemtype con = 0;selemtype val1 = GetTop(ps); // 数值Pop(ps);selemtype val2 = 0;while (JudgeOverflow(ps)){switch ((char)GetTop(ps)){case'+':{Pop(ps); // 将 '+' 出栈val2 = GetTop(ps);Pop(ps);con += val1;val1 = val2;}break;case '-':{Pop(ps);if (!JudgeOverflow(ps)){con -= val1;return con;}val2 = GetTop(ps);Pop(ps);con -= val1;val1 = val2;}break;case '*':{Pop(ps);val1 *= GetTop(ps);Pop(ps);}break;case '/':{Pop(ps);val2 = GetTop(ps);Pop(ps);if (GetTop(ps) == '/')val1 = val1 * val2;elseval1 = val2 / val1;}break;case '(':Pop(ps);goto end;}}end:con += val1;return con;}selemtype Expression(char* str)
{SQS s;Init(&s);double con = 0; // 表达式结果char* p = NULL; // int i = 0;for (i = 0; str[i]; ) // 遍历字符串{if (isdigit(str[i])) // 数值{selemtype val = strtod(&str[i], &p);Push(&s, val);i = p - str; // 更新下标}else  // 符号{if (str[i] != ')'){Push(&s, str[i]);}else{con = Evaluation(&s);Push(&s, con);}i++; // 更新下标}}con = Evaluation(&s);Destroy(&s);return con;
}int main()
{while (1){printf("请输入表达式:");char str[100] = { 0 };scanf("%s", str);selemtype con = 0;//表达式结果con = Expression(str);printf("表达式结果为:%.2lf\n\n", con);}return 0;
}

运行实例:

在这里插入图片描述


总结

以上就是表达式求值的全部内容,熊猫这边建议大家在学习的过程中可以尝试着做一些有趣的东西,这样不仅能够提高自己的学习积极性,对自己的实力也能做到很好的打磨,何乐而不为呢~。
那么今天就讲解到这里,如果有什么疑问或者建议都可以在评论区留言,感谢大家对在这里插入图片描述的支持。

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
苁蓉的食用方法 鲜苁蓉的吃法有... 肉苁蓉号称“温而不热,补而不峻,暖而不燥,滑而不泄,故有从容之名。”历来也是传统药食同源的滋补佳品。...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
阳澄湖在哪里哪个省的 阳澄湖是... 点击题目下方苏州生活指南有用 有趣 有态度近期的阳澄湖度很高,3月24日,央视二套《第一时间》直播连...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...