逆波兰表达式求值
中缀,前缀(波兰),后缀(逆波兰)表达式的基本概念
- 逆波兰表达式即所谓的后缀表达式
- 我们常见的数学表达式如:
3 + 4、5 + (6 -3) * 2
……等,都是中缀表达式,中缀表达式的操作符位于操作数的中间- 中缀表达式:也叫波兰表达式,如
- × + 3 4 5 6
,前缀表达式的操作符位于操作数的前面- 后缀表达式:也叫逆波兰表达式,如
3 4 + 5 × 6 -
,后缀表达式的操作符位于操作数的后面
逆波兰表达式的优点和计算方法
优点
- 省略了括号,使表达式更加简洁明了,减少了歧义。
- 方便计算机进行计算,因为计算机可以使用栈来实现对后缀表达式的计算,而不需要使用递归或复杂的算法。
- 可以避免运算符优先级的问题,因为后缀表达式中运算符的优先级是通过运算符在表达式中的位置来确定的,而不是通过运算符本身的优先级来确定的。
- 可以方便地转换成前缀表达式,因为后缀表达式的计算顺序与前缀表达式的计算顺序相反,只需将后缀表达式中的操作数和运算符反转即可得到前缀表达式。
计算方法
- 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
思路
函数原型
int evalRPN(char** tokens, int tokensSize)
- 可能有小伙伴会疑惑,为什么要传入二级指针呢?tokens存储的是数字字符‘0’ ~ ‘9’,和操作符字符‘+’, ‘-’ ,‘*’, ‘/’,我们又不改变数组的值,只是对其进行解引用,为什么要传入二级指针呢?其实,大家可能忽略了一点,即被操作的数字不一定是个位数, 如果被操作的是一个十位数及其以上的数,那么就需要一个字符串来存储这多个数字字符,继而我们可以得到,我们需要用一个字符指针数组来存储这多个字符串。
- tokensSize就是字符串指针数组中字符串的个数
如何将数字入栈
- 如果被操作数是一个个位数,那么好办,用
StackPush(st, tokens[i][0] - '0')
这个操作就好了(StackPush是入栈函数)- 但如果被操作数是以一个十位数及其以上的数字呢?那我们就要对多个字符进行处理,如果是一个负数就更麻烦了。那有没有简单的操作呢?
- 这里我们就需要用到==库函数
atoi()
==了
库函数atoi()
- 函数原型
int atoi(const char *str )
- str即传入的字符串
- 函数功能:把字符串转换成整型数
- 返回值:此值由将输入字符作为数字解析而生成。 如果该输入无法转换为该类型的值,则atoi的返回值为 0
- 注:该函数的返回值是int类型,因此传入的字符串所表示的值不能超过int所能表示的范围
- EG:
#include <stdio.h> #include <stdlib.h> int main() { int a; char *ptr1 = "3124"; char *ptr2 = "0"; char *ptr3 = "12.33"; char *ptr4 = "-1245"; char *ptr5 = "+21"; char *ptr6 = "s3241"; char *ptr7 = "*3241"; char *ptr8 = "/3241"; a = atoi(ptr1); printf("\"%s\"\t-> %d\n",ptr1, a); a = atoi(ptr2); printf("\"%s\"\t-> %d\n",ptr2, a); a = atoi(ptr3); printf("\"%s\"\t-> %d\n",ptr3, a); a = atoi(ptr4); printf("\"%s\"\t-> %d\n",ptr4, a); a = atoi(ptr5); printf("\"%s\"\t-> %d\n",ptr5, a); a = atoi(ptr6); printf("\"%s\"\t-> %d\n",ptr6, a); a = atoi(ptr7); printf("\"%s\"\t-> %d\n",ptr7, a); a = atoi(ptr8); printf("\"%s\"\t-> %d\n",ptr8, a); return 0; }
- 可以得到如下结果:
- 缺陷分析:我们可以看到,如果传入的是无效的字符串,那么返回值就是0,那么在不知道传入的字符串是否有效的情况下,我们就会不知道字符串到底是无效字符串还是本来是是字符串“0”。
实现代码
- 有了以上的准备,这题就好写了,直接上代码。
#include<assert.h> typedef struct Stack { int* stack; int top; }ST; //判断栈空 bool StackEmpty(ST *st) { return st->top == 0; } //初始化栈 void StackInit(ST* st, int len) { st->stack = (int*)malloc(sizeof(int) * len); st->top = 0; } //入栈 void StackPush(ST* st, int val) { st->stack[st->top] = val; st->top++; } //出栈,并返回栈元素 int StackPop(ST* st) { assert(!StackEmpty(st)); st->top--; return st->stack[st->top]; } int evalRPN(char** tokens, int tokensSize) { ST* st = (ST*)malloc(sizeof(ST)); //申请栈内存 StackInit(st, tokensSize); //初始化栈 for (int i = 0; i < tokensSize; i++) { //如果是操作符,并且字符串的长度为1(排除负数的情况) if (strlen(tokens[i]) == 1 && (tokens[i][0] == '+'|| tokens[i][0] == '-' || tokens[i][0] == '*' || tokens[i][0] == '/')) { //取出栈顶的两个元素 int num_1 = StackPop(st); int num_2 = StackPop(st); switch (tokens[i][0]) { case '+': StackPush(st, num_2 + num_1); break; case '-': StackPush(st, num_2 - num_1); break; case '*': StackPush(st, num_2 * num_1); break; case '/': StackPush(st, num_2 / num_1); break; } } //如果是操作数 else { StackPush(st, atoi(tokens[i])); //利用atoi库函数,直接将字符串转换为数字,入栈 } } //返回最后的结果 return st->stack[--(st->top)]; }
tips:本篇对库函数atoi()
的解析,对atoi()函数解析以及缺陷分析,以及对atoi()、atof()的改造这篇博文多有参考,如果读者想对这个库函数有更深的了解,可以去看看大佬写的博客。