
Hello,大家好,国庆的第二天,带来的是数据结构中堆栈部分的后缀表达式,这也是一块有关栈的应用方面困扰了众多同学的一个大难题,今天就让我们一起解决这个难题📕
堆栈应用之后缀表达式
后缀表达式也称为逆波兰表达式,也就是将算术运算符放在操作数的后面
相信这部分是大家最难过的一道坎,让我们来一探究竟🔍
首先,最重要的一点是你必须知道转换的规则
📚法则一:如果遇到一个运算符op以及左括号“(”,此时栈如果为空,则直接将其进栈
📚法则二:若是遇到了操作数num,就将其直接输出
📚法则三:如果栈不为空,则只有当op的优先级高于栈顶运算符优先级时才将其进栈,否则将栈顶op弹出
📚法则四:若当前op进栈时发现栈中有与之相同op,则出栈其一,再加当前op压入栈中
📚法则五:若遇到右括号,则开始弹出栈字符,直到左括号为止
📚法则六:只要当遇到右括号“)”时,才从栈中弹出左括号“(”,否则一直遍历
有了固定的发展,接下来就是具体的案例和分析
Part1
Part2
【5 + 8*2 + ( 3 * 9 + 6 )*4】
Part3
【5 + 8*2 + ( 3 * 9 + 6 )*4】
Part4
【5 + 8*2 + ( 3 * 9 + 6 )*4】

Part5
【5 + 8*2 + ( 3 * 9 + 6 )*4】
Part6
【5 + 8*2 + ( 3 * 9 + 6 )*4】
了解了堆栈的基本原理后,接下来就是代码环节,也是可视化计算的重要一趴
void trans(char* exp, char postexp[]) //将算术表达式exp转换成后缀表达式postexp
{
char e;
SqStack* Optr; //定义运算符栈
InitStack(Optr); //初始化运算符栈
int i = 0; //i作为postexp的下标
while (*exp != '\0') //exp表达式未扫描完时循环
{
switch (*exp)
{
case '(': //判定为左括号
Push(Optr, '('); //左括号进栈
exp++; //继续扫描其他字符
break;
case ')': //判定为右括号
Pop(Optr, e); //出栈元素e
while (e != '(') //不为'('时循环
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //继续出栈元素e
}
exp++; //继续扫描其他字符
break;
case '+': //判定为加或减号
case '-':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e != '(') //e不是'('
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e是'(时退出循环
break;
}
Push(Optr, *exp); //将'+'或'-'进栈
exp++; //继续扫描其他字符
break;
case '*': //判定为'*'或'/'号
case '/':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e == '*' || e == '/') //将栈顶'*'或'/'运算符出栈并存放到postexp中
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e为非'*'或'/'运算符时退出循环
break;
}
Push(Optr, *exp); //将'*'或'/'进栈
exp++; //继续扫描其他字符
break;
default: //处理数字字符
while (*exp >= '0' && *exp <= '9') //判定为数字
{
postexp[i++] = *exp;
exp++;
}
postexp[i++] = ' '; //用#标识一个数值串结束
}
}
while (!StackEmpty(Optr)) //此时exp扫描完毕,栈不空时循环
{
Pop(Optr, e); //出栈元素e
postexp[i++] = e; //将e存放到postexp中
}
postexp[i] = '\0'; //给postexp表达式添加结束标识
DestroyStack(Optr); //销毁栈
}
看完这串代码,不用猜,我知道你已经已经想点下浏览器中这个网页的【×】了。但是何妨不听我讲完再说
case '(': //判定为左括号
Push(Optr, '('); //左括号进栈
exp++; //继续扫描其他字符
break;
case ')': //判定为右括号
Pop(Optr, e); //出栈元素e
while (e != '(') //不为'('时循环
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //继续出栈元素e
}
exp++; //继续扫描其他字符
break;
case '+': //判定为加或减号
case '-':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e != '(') //e不是'('
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e是'(时退出循环
break;
}
Push(Optr, *exp); //将'+'或'-'进栈
exp++; //继续扫描其他字符
break;
case '*': //判定为'*'或'/'号
case '/':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e == '*' || e == '/') //将栈顶'*'或'/'运算符出栈并存放到postexp中
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e为非'*'或'/'运算符时退出循环
break;
}
Push(Optr, *exp); //将'*'或'/'进栈
exp++; //继续扫描其他字符
break;
default: //处理数字字符
while (*exp >= '0' && *exp <= '9') //判定为数字
{
postexp[i++] = *exp;
exp++;
}
postexp[i++] = ' '; //用空格标识一个数值串结束
while (!StackEmpty(Optr)) //此时exp扫描完毕,栈不空时循环
{
Pop(Optr, e); //出栈元素e
postexp[i++] = e; //将e存放到postexp中
}
postexp[i] = '\0'; //给postexp表达式添加结束标识
DestroyStack(Optr); //销毁栈
看完了上面的细致讲解,难道你还想退出吗,不想的话就继续跟着我来吧🚶
将中缀表达式转换为后缀表达式后,只是完成了我们的第一步,也就是有了一个模型了,接下去就要将其细心雕刻直至完美🍁
首先一样,我们要了解一下其运算规则
double compvalue(char* postexp) //计算后缀表达式的值
{
double d, a, b, c, e;
SqStack1* Opnd; //定义操作数栈
InitStack1(Opnd); //初始化操作数栈
while (*postexp != '\0') //postexp字符串未扫描完时循环
{
switch (*postexp)
{
case '+': //判定为'+'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
c = b + a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
case '-': //判定为'-'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
c = b - a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
case '*': //判定为'*'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
c = b * a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
case '/': //判定为'/'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
if (a != 0)
{
c = b / a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
}
else
{
printf("\n\t除零错误!\n");
exit(0); //异常退出
}
break;
default: //处理数字字符
d = 0; //将连续的数字字符转换成对应的数值存放到d中
while (*postexp >= '0' && *postexp <= '9') //判定为数字字符
{
d = 10 * d + *postexp - '0';
postexp++;
}
Push1(Opnd, d); //将数值d进栈
break;
}
postexp++; //继续处理其他字符
}
GetTop1(Opnd, e); //取栈顶元素e
DestroyStack1(Opnd); //销毁栈
return e; //返回e
}
首先,就是要在main函数中,传入对应的exp和postexp
char exp[] = "(56 - 20)/(4 + 2)"; //错误!!!
char exp[] = "(56-20)/(4+2)"; //正确!!!
char exp[] = "5+8*2+(3*9+6)*4";
char postexp[MaxSize];
trans(exp, postexp);
printf("中缀表达式为:%s\n", exp);
printf("后缀表达式为:%s\n", postexp);
printf("表达式的值为:%g\n", compvalue(postexp));
这就是最终的结果,看后缀表达式,和我们上面通过堆栈原理分析的完全吻合,大家算出来是这样吗😃
//求简单表达式的值
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100
//---------------------------------------------------------
//--运算符栈基本运算---------------------------------------
//---------------------------------------------------------
typedef struct
{
char data[MaxSize]; //存放运算符
int top; //栈顶指针
} SqStack;
void InitStack(SqStack*& s) //初始化栈
{
s = (SqStack*)malloc(sizeof(SqStack));
s->top = -1;
}
void DestroyStack(SqStack*& s) //销毁栈
{
free(s);
}
bool StackEmpty(SqStack* s) //判断栈是否为空
{
return(s->top == -1);
}
bool Push(SqStack*& s, char e) //进栈元素e
{
if (s->top == MaxSize - 1)
return false;
s->top++;
s->data[s->top] = e;
return true;
}
bool Pop(SqStack*& s, char& e) //出栈元素e
{
if (s->top == -1)
return false;
e = s->data[s->top];
s->top--;
return true;
}
bool GetTop(SqStack* s, char& e) //取栈顶元素e
{
if (s->top == -1)
return false;
e = s->data[s->top];
return true;
}
//---------------------------------------------------------
void trans(char* exp, char postexp[]) //将算术表达式exp转换成后缀表达式postexp
{
char e;
SqStack* Optr; //定义运算符栈
InitStack(Optr); //初始化运算符栈
int i = 0; //i作为postexp的下标
while (*exp != '\0') //exp表达式未扫描完时循环
{
switch (*exp)
{
case '(': //判定为左括号
Push(Optr, '('); //左括号进栈
exp++; //继续扫描其他字符
break;
case ')': //判定为右括号
Pop(Optr, e); //出栈元素e
while (e != '(') //不为'('时循环
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //继续出栈元素e
}
exp++; //继续扫描其他字符
break;
case '+': //判定为加或减号
case '-':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e != '(') //e不是'('
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e是'(时退出循环
break;
}
Push(Optr, *exp); //将'+'或'-'进栈
exp++; //继续扫描其他字符
break;
case '*': //判定为'*'或'/'号
case '/':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e == '*' || e == '/') //将栈顶'*'或'/'运算符出栈并存放到postexp中
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e为非'*'或'/'运算符时退出循环
break;
}
Push(Optr, *exp); //将'*'或'/'进栈
exp++; //继续扫描其他字符
break;
default: //处理数字字符
while (*exp >= '0' && *exp <= '9') //判定为数字
{
postexp[i++] = *exp;
exp++;
}
postexp[i++] = ' '; //用空格标识一个数值串结束
}
}
while (!StackEmpty(Optr)) //此时exp扫描完毕,栈不空时循环
{
Pop(Optr, e); //出栈元素e
postexp[i++] = e; //将e存放到postexp中
}
postexp[i] = '\0'; //给postexp表达式添加结束标识
DestroyStack(Optr); //销毁栈
}
//---------------------------------------------------------
//--操作数栈基本运算---------------------------------------
//---------------------------------------------------------
typedef struct
{
double data[MaxSize]; //存放数值
int top; //栈顶指针
} SqStack1;
void InitStack1(SqStack1*& s) //初始化栈
{
s = (SqStack1*)malloc(sizeof(SqStack1));
s->top = -1;
}
void DestroyStack1(SqStack1*& s) //销毁栈
{
free(s);
}
bool StackEmpty1(SqStack1* s) //判断栈是否为空
{
return(s->top == -1);
}
bool Push1(SqStack1*& s, double e) //进栈元素e
{
if (s->top == MaxSize - 1)
return false;
s->top++;
s->data[s->top] = e;
return true;
}
bool Pop1(SqStack1*& s, double& e) //出栈元素e
{
if (s->top == -1)
return false;
e = s->data[s->top];
s->top--;
return true;
}
bool GetTop1(SqStack1* s, double& e) //取栈顶元素e
{
if (s->top == -1)
return false;
e = s->data[s->top];
return true;
}
//---------------------------------------------------------
double compvalue(char* postexp) //计算后缀表达式的值
{
double d, a, b, c, e;
SqStack1* Opnd; //定义操作数栈
InitStack1(Opnd); //初始化操作数栈
while (*postexp != '\0') //postexp字符串未扫描完时循环
{
switch (*postexp)
{
case '+': //判定为'+'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
c = b + a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
case '-': //判定为'-'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
c = b - a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
case '*': //判定为'*'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
c = b * a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
case '/': //判定为'/'号
Pop1(Opnd, a); //出栈元素a
Pop1(Opnd, b); //出栈元素b
if (a != 0)
{
c = b / a; //计算c
Push1(Opnd, c); //将计算结果c进栈
break;
}
else
{
printf("\n\t除零错误!\n");
exit(0); //异常退出
}
break;
default: //处理数字字符
d = 0; //将连续的数字字符转换成对应的数值存放到d中
while (*postexp >= '0' && *postexp <= '9') //判定为数字字符
{
d = 10 * d + *postexp - '0';
postexp++;
}
Push1(Opnd, d); //将数值d进栈
break;
}
postexp++; //继续处理其他字符
}
GetTop1(Opnd, e); //取栈顶元素e
DestroyStack1(Opnd); //销毁栈
return e; //返回e
}
void test()
{
char exp[] = "5+8*2+(3*9+6)*4";
char postexp[MaxSize];
trans(exp, postexp);
printf("中缀表达式为:%s\n", exp);
printf("后缀表达式为:%s\n", postexp);
printf("表达式的值为:%g\n", compvalue(postexp));
}
int main(void)
{
test();
return 0;
}
明白了如何将一个中缀表达式转换为后缀表达式,并且清楚了如何求值。接下来让我们到力扣上去找道题去练练手吧
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = [“4”,“13”,“5”,“/”,“+”]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = [“10”,“6”,“9”,“3”,“+”,“-11”,““,”/“,””,“17”,“+”,“5”,“+”]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
温馨提示,微信手机端看不到
逆波兰表达式
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(int i = 0;i < tokens.size(); ++i)
{
if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/")
{
int num1 = st.top(); st.pop();
int num2 = st.top(); st.pop();
if(tokens[i] == "+") st.push(num2 + num1);
if(tokens[i] == "-") st.push(num2 - num1);
if(tokens[i] == "*") st.push((long long)num2 * num1);
if(tokens[i] == "/") st.push(num2 / num1);
}
else
{
//若为数字字符,则将其转换为数字,方便后续运算
st.push(stoi(tokens[i]));
}
}
int ret = st.top();
st.pop();
return ret;
}
};
代码很简单,就是去遍历给出的这个字符串,判断其为加减乘数四个运算符,是的话就将栈中的头两个元素出栈,进行一个四则运算,但是这里要注意的是因为展示FILO,
你第一个进栈的,也就是第二个出栈的才是第一个操作符
你第二个进栈的,也就是你第一个出栈的是第二个操作符
这个逻辑一定要理清,否则会出现运算问题,可以看到这里的运算num2都是在num1的前面
若不是四个运算符,则将其转换为数字进栈即可,这里用到了一个比较偏僻的函数stoi(),它可以将一个字符转换为数字形式,大家不清楚的可以去了解一下
OK,这就是我们今天要讲解的内容,有关数据结构中堆栈应用方面——后缀表达式,你学废了吗🎓
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
我正在尝试在Rails上安装ruby,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf