前中后缀表达式的转换和运算

0.知识点

1.scanf 和 sscanf 的区别 及 sscanf 的应用,注意*第二参数不能加.表示精度——2.1.1.3
2.判断字符是否是数字——2.1.1.2
3.转换

  • 字符型 转为 整型(ascii to int):atoi
  • 字符型 转为 长整型(ascii to int64):_atoi64
  • 字符型 转为 任意:sscanf——2.1.1.3;2.1.2
  • 长整型 转为 字符型:_i64toa
  • 整型 转为 字符型:itoa
    4.字符串清零,用fill,用memset好像不行:fill(polish, polish + strlen(polish), 0);——1.1.1.2;1.2.1.2

在这里插入图片描述

1.波兰表达式和逆波兰表达式

常见的算术表达式,称为中缀表达式,例如:

5 + ( 6 - 4 / 2 ) * 3

1.1波兰表达式

波兰表达式也称为前缀表达式,以上面的例子为例,其波兰表达式为:

+ 5 * - 6 / 4 2 3

1.1.0 手写的转换和计算的技巧

  1. (手写)中缀转化为前缀就是从左往右扫描,先存入第一个表达式的操作符,然后存入操作符左边的表达式(表达式中的存取顺序递归),再存入右边的表达式
  2. (手写运算):先逆转,从左往右扫描,遇到操作符,取前两个操作数运算(前数y后数x操作符/:yx/即为x/y

1.1.1中缀表达式转换前缀表达式的操作过程

(1)首先设定一个操作符栈,从右到左顺序扫描整个中缀表达式:

1.1.1.0结构体
char strop[300], str[1000],polish[1000];//操作符栈,输入的字符串,波兰式栈
int topop,toppol;//操作符,波兰式栈的栈顶指针(指向栈顶元素,-1表示栈空)
1.1.1.1运算
  • 如果是操作数,则直接归入前缀表达式;
  • 如果是括号
    Ⅰ如果是右括号,则直接将其入栈;
    Ⅱ如果是左括号,则将栈中的操作符依次弹栈,归入前缀表达式,直至遇到右括号,将右括号弹栈,处理结束;
  • 如果是其他操作符,则检测栈顶操作符的优先级与当前操作符的优先级关系
    Ⅰ如果栈顶操作符不是操作符,那么直接压栈。例如栈顶是左括号 ),或者栈为空。
    Ⅱ如果栈顶操作符优先级小于等于当前操作符优先级,这时将当前操作符压栈(同Ⅰ,所以可以写优先级函数,大于为true,否则为false)。
    Ⅲ如果栈顶操作符优先级大于当前操作符的优先级,则弹栈,并归入前缀表达式,重复扫描这个字符

(2)当扫描完毕整个中缀表达式后,检测操作符栈是否为空,如果不为空,则依次将栈中操作符弹栈,归入前缀表达式。

(3)最后,将前缀表达式翻转,得到中缀表达式对应的前缀表达式。
在这里插入图片描述

void POLISH(){
	int n = strlen(str);//总长
	reverse(str, str+n);//反转。为了从右往左扫描

	char t,tmp;//暂存的当前字符,弹栈时的暂用字符
	topop = -1; toppol = -1;//设栈空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作数
			polish[++toppol] = t;
		}
		else if (t == ')'){//右括号
			strop[++topop] = t;
		}
		else if (t == '('){//左括号
			while (1){
				tmp = strop[topop--];//弹栈
				if (tmp == ')'){
					break;
				}
				else if (topop != -1){//未弹完
					polish[++toppol] = tmp;
				}
				else
					return;
			}
		}
		else if(t=='*'||t=='/'||t=='+'||t=='-'){//操作符
			if (!priority(strop[topop], t)||topop==-1)
				strop[++topop] = t;
			else{
				tmp = strop[topop--];//弹栈
				polish[++toppol] = tmp;
				i--;
			}
		}
		else
			continue;
	}
	while (topop != -1){
		tmp = strop[topop--];//弹栈
		polish[++toppol] = tmp;
	}
	reverse(polish, polish + strlen(polish));
	return;
}
1.1.1.2AC代码
#include <stdio.h>
#include<iostream>
#include <string>
#include<stdlib.h>
#include <ctype.h>
#include<algorithm>
using namespace std;

char strop[300], str[1000],polish[1000];//操作符栈,输入的字符串,波兰式栈
int topop,toppol;//操作符,波兰式栈的栈顶指针(指向栈顶元素,-1表示栈空)

bool priority(char a, char b){//前后(a,b)优先级大小比较(大于true,否则false)
	if (a == '*' || a == '/')
		return true;
	else if (a == '+' || a == '-'){
		if (b == '+' || b == '-')
			return true;
		return false;
	}
	else//a为定界符
		return false;
}
void POLISH(){
	int n = strlen(str);//总长
	reverse(str, str+n);//反转。为了从右往左扫描

	char t,tmp;//暂存的当前字符,弹栈时的暂用字符
	topop = -1; toppol = -1;//设栈空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作数
			polish[++toppol] = t;
		}
		else if (t == ')'){//右括号
			strop[++topop] = t;
		}
		else if (t == '('){//左括号
			while (1){
				tmp = strop[topop--];//弹栈
				if (tmp == ')'){
					break;
				}
				else if (topop != -1){//未弹完
					polish[++toppol] = tmp;
				}
				else
					return;
			}
		}
		else if(t=='*'||t=='/'||t=='+'||t=='-'){//操作符
			if (!priority(strop[topop], t)||topop==-1)
				strop[++topop] = t;
			else{
				tmp = strop[topop--];//弹栈
				polish[++toppol] = tmp;
				i--;
			}
		}
		else
			continue;
	}
	while (topop != -1){
		tmp = strop[topop--];//弹栈
		polish[++toppol] = tmp;
	}
	reverse(polish, polish + strlen(polish));
	return;
}
int main()
{
	cin.getline(str, sizeof(str));
	while (str != NULL && strcmp(str, "0") != 0){//输入,非空且非0(输入了0不必运算)
		POLISH();
		printf("%s\n", polish);
		//memset(polish, 0, sizeof(char));
		fill(polish, polish + strlen(polish), 0);
		cin.getline(str, sizeof(str));
	}

	system("pause");
	return 0;
}

1.2逆波兰表达式

逆波兰表达式也称为后缀表达式,以上面的例子为例,其逆波兰表达式为:

5 6 4 2 / - 3 * +

1.2.0 手写的转换和计算的技巧

  1. (手写)中缀转化为前缀就是从左往右扫描,遇数就存直到遇见第一个操作符,先取其右表达式(右表达式的取法递归)再取该操作符
  2. (手写运算):从左往右扫描,遇到操作符,取前两个操作数运算(前数x后数y操作符/:xy/即为x/y

1.2.1中缀表达式转换后缀表达式的操作过程

1.2.1.0结构体

你需要设定一个栈strop,和一个线性表 repolish 。
strop用于临时存储运算符和左括号分界符( ,repolish用于存储后缀表达式。

char strop[300], str[1000], repolish[1000];//操作符栈,输入的字符串,逆波兰式栈
int topop, toprepol;//操作符,逆波兰式栈的栈顶指针(指向栈顶元素,-1表示栈空)
1.2.1.1运算

遍历原始表达式中的每一个表达式元素
(1)如果是操作数,则直接追加到 repolish中。只有 运算符 或者 分界符( 才可以存放到 栈strop中
(2)如果是分界符

  • 如果不是运算符,则二者没有可比性,则直接将此运算符op1压栈。 例如栈顶是左括号 ( ,或者栈为空。
  • 如果是左括号 ( , 则 直接压入strop,等待下一个最近的 右括号 与之配对。
  • 如果是右括号),则说明有一对括号已经配对(在表达式输入无误的情况下)。不将它压栈,丢弃它,然后从strop中出栈,得到元素tmp,将tmp依次追加到L里。一直循环,直到出栈元素tmp是 左括号 ( ,同样丢弃他。

(3)如果是运算符(用op2表示)

  • 如果strop栈顶元素(用op1表示) 不是运算符,则二者没有可比性,则直接将此运算符op2压栈。 例如栈顶是左括号 ( ,或者栈为空。
  • 如果strop栈顶元素(用op1表示) 是运算符 ,则比较op1和 op2的优先级。如果op1(前) < op2(后) ,则直接将此运算符op2压栈。
  • 如果不满足op1(前) < op2(后),则将op1出栈,并追加到repolish,重复步骤3。
  • 也就是说,如果在strop栈中,有2个相邻的元素都是运算符,则他们必须满足:下层运算符的优先级一定小于上层元素的优先级,才能相邻。

(4)最后,如果strop中还有元素,则依次弹出追加到repolish后,就得到了后缀表达式。

void RePolish(){
	int n = strlen(str);//总长

	char t, tmp;//暂存的当前字符,弹栈时的暂用字符
	topop = -1; toprepol = -1;//设栈空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作数
			repolish[++toprepol] = t;
		}
		else if (t == '('){//左括号
			strop[++topop] = t;
		}
		else if (t == ')'){//右括号
			while (1){
				tmp = strop[topop--];//弹栈
				if (tmp == '(')
					break;
				else if (topop != -1){//未弹完
					repolish[++toprepol] = tmp;
				}
				else{//全弹完了还没遇到(
					return;
				}
			}
		}
		else if (t == '*' || t == '/' || t == '+' || t == '-'){//操作符
			if (!(strop[topop] == '*' || strop[topop] == '/' || strop[topop] == '+' || strop[topop] == '-')||topop==-1){
				strop[++topop] = t;
			}
			else if (!priority(strop[topop], t)){
				strop[++topop] = t;
			}
			else{
				tmp = strop[topop--];//弹栈
				repolish[++toprepol] = tmp;
				i--;//下一循环仍然判断此数
			}
		}
		else
			continue;
	}
	while (topop>-1){
		tmp = strop[topop--];//弹栈
		repolish[++toprepol] = tmp;
	}
	return;
}
1.2.1.2AC代码
#include <stdio.h>
#include<iostream>
#include <string>
#include<stdlib.h>
#include <ctype.h>
#include<algorithm>
using namespace std;

char strop[300], str[1000], repolish[1000];//操作符栈,输入的字符串,逆波兰式栈
int topop, toprepol;//操作符,逆波兰式栈的栈顶指针(指向栈顶元素,-1表示栈空)

bool priority(char a, char b){//前后(a,b)优先级大小比较(大于true,小于等于false)
	if (a == '*' || a == '/')
		return true;
	else{
		if (b == '+' || b == '-')
			return true;
		else
		return false;
	}
}
void RePolish(){
	int n = strlen(str);//总长

	char t, tmp;//暂存的当前字符,弹栈时的暂用字符
	topop = -1; toprepol = -1;//设栈空

	for (int i = 0; i<n; i++){
		t = str[i];
		if (isdigit(t)){//操作数
			repolish[++toprepol] = t;
		}
		else if (t == '('){//左括号
			strop[++topop] = t;
		}
		else if (t == ')'){//右括号
			while (1){
				tmp = strop[topop--];//弹栈
				if (tmp == '(')
					break;
				else if (topop != -1){//未弹完
					repolish[++toprepol] = tmp;
				}
				else{//全弹完了还没遇到(
					return;
				}
			}
		}
		else if (t == '*' || t == '/' || t == '+' || t == '-'){//操作符
			if (!(strop[topop] == '*' || strop[topop] == '/' || strop[topop] == '+' || strop[topop] == '-')||topop==-1){
				strop[++topop] = t;
			}
			else if (!priority(strop[topop], t)){
				strop[++topop] = t;
			}
			else{
				tmp = strop[topop--];//弹栈
				repolish[++toprepol] = tmp;
				i--;//下一循环仍然判断此数
			}
		}
		else
			continue;
	}
	while (topop>-1){
		tmp = strop[topop--];//弹栈
		repolish[++toprepol] = tmp;
	}
	return;
}
int main()
{
	cin.getline(str, sizeof(str));
	while (str != NULL && strcmp(str, "0") != 0){//输入,非空且非0(输入了0不必运算)
		RePolish();
		printf("%s\n", repolish);

		fill(repolish, repolish + strlen(repolish), 0);
		cin.getline(str, sizeof(str));
	}

	system("pause");
	return 0;
}

1.2.2优势

它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:
如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

2.运算

2.1中缀表达式的运算

写了两个数组stDit(栈顶指针即下标top1),stOp(栈顶指针即下标top2)作为操作数,操作符的顺序栈

2.1.0结构体(顺序栈)


double stDit[300];//操作数栈(顺序结构)
char stOp[300], str[300];//操作符栈,输入的字符串
int top1, top2;//操作数栈的栈顶指针,操作符栈的栈顶指针(就是数组(栈)的下标)

2.1.1运算

double Calc()
{
	double x, y, tmp;
	char op;
	int i, n = strlen(str);

	top1 = top2 = -1;//栈顶为-1表示栈空(下标为0就是有一个栈内元素了)
	stOp[++top2] = '#';//操作符栈压入一个#界定符
	str[n++] = '#';//输入的字符串最后插上一个界定符(这样读取到最后和一开始压入的界定符就匹配成一对了)

	for (i = 0; i < n; ++i)
	{
		if (str[i] == ' ')
			continue;
		if (isdigit(str[i]))				//是数字
		{
			sscanf(str + i, "%lf", &tmp);//从子字符串中str+i开始读取一个为%lf型的值  ==(只要符合  能转化为%lf型  的条件就自动往下一个地址找直到不符合条件)== 赋给tmp
			stDit[++top1] = tmp;			//stack push
			//跳过后头的数字直到非数字
			while (isdigit(str[i + 1]))		//+1很重要
				++i;
		}
		else								//是操作符
		{
			while (Priority(stOp[top2]) >= Priority(str[i]))//如果操作栈顶的操作符优先级高,则作+-*/运算(直到新操作符优先级高)
			{
				if (str[i] == '#' && stOp[top2] == '#')//扫描完毕
					return stDit[top1];
				y = stDit[top1--];//取栈顶操作数(x是前一个数,y在x后头)
				x = stDit[top1--];//取再前一个的操作数
				op = stOp[top2--];//取栈顶操作符
				stDit[++top1] = Operate(x, y, op);//计算结果入栈
			}
			stOp[++top2] = str[i];			//如果新操作符优先级高,str[i]进栈
		}
	}
	return stDit[top1];
}

设变量
bool pipei 是 是否是合法的表达式的 标识符
double x,y 是 做基本运算时出栈的两个暂存的数
char op 是 做基本运算时出栈的暂存的字符
double tmp 是 判断子字符串为数字后,取出转而放入的暂存的数
int i, n = strlen(str) 是 遍历字符串,当前字符是第i个,总长是n个

top1 = top2 = -1;//栈顶为-1表示栈空(下标为0就是有一个栈内元素了)
	stOp[++top2] = '#';//操作符栈压入一个#界定符
	str[n++] = '#';//输入的字符串最后插上一个界定符(这样读取到最后和一开始压入的界定符就匹配成一对了)

遍历字符串

  1. 如果是空格
    跳过

  2. 如果是数字
    ——2.1从子字符串中str+i开始读取一个为%lf型的值 (只要符合 能转化为%lf型 的条件就自动往下一个地址找直到不符合条件) 赋给tmp
    sscanf(str + i, “%lf”, &tmp);
    ——2.2操作数栈stDit栈顶指针先+1,再让tmp入栈
    ——2.3跳过后头的数字直到非数字(连续的数字都在sscanf函数中把值赋给tmp并入栈,已经使用过了)

  3. 如果不是数字,则为操作符
    ——3.1判断优先级
    ——3.2若小于,则操作数入栈
    ——3.3若大于,取出操作数栈顶的两个操作数放入x,y(x是前一个数,y在x后头)和操作符栈顶的一个操作符;运算并让计算结果入栈;i–使得下一次循环仍要比较当前字符(当前字符还未用)
    ——3.4若等于,如果操作符栈顶元素为’(‘且当前字符为’)’,则让‘(’出栈;如果操作符栈顶元素为’#‘且当前字符为’#’,则运算结束,返回操作数栈顶元素(当前应只有一个元素了,即最终结果);否则异常标识符赋false并返回

  4. 否则异常标识符赋false并返回

如果遍历完了还未返回同样异常,异常标识符赋false并返回

2.1.1.2判断数字:isdigit
if (isdigit(str[i]))

头文件#include <ctype.h>
如果是数字,返回true
否则,返回false

2.1.1.3从字符串中读取:sscanf的运用

sscanf和scanf的区别:
scanf从键盘(stdin)的缓存区中读取,赋值到一个或多个数据类型中
sscanf从字符串中读取,赋值到一个或多个数据类型中

  1. 函数语法编辑
    int sscanf( const char *buffer, const char *format, [ argument ] … );
    其中
    buffer 字符串(也可以是子字符串)
    format 赋值到一个或多个数据类型中 “····”
    argument 可选自变量(一个或多个)注意是地址,若有变量名一定要加&取地址符

  2. 第二个参数可以是一个或多个 {%[*] [width] [{h | I | I64 | L}]type | ’ ’ | ‘\t’ |
    ‘\n’ | 非%符号}
    注:
    (1) * 亦可用于格式中, (即 %d 和 %s) 加了星号 () 表示跳过此数据不读入. (也就是不把此数据读入参数中)
    (2){a|b|c}表示a,b,c中选一,[d],表示可以有d也可以没有d。
    (3)width表示读取宽度。
    (4){h | l | I64 | L}:参数的size,通常h表示单字节size,I表示2字节 size,L表示4字节size(double例外),l64表示8字节size。
    (5)type :这就很多了,就是%s,%d之类。
    (6)特别的:%
    [width] [{h | l | I64 | L}]type 表示满足该条件的被过滤掉,不会向目标参数中写入值
    失败返回0 ,否则返回格式化的参数个数
    (7)如果读取的字符串,不是以空格来分隔的话,就可以使用%[]。
    (8)注意这里的第二个参数不能加.*表示精确度,否则该函数无用

  3. 返回值
    函数将返回成功赋值的字段个数;返回值不包括已读取但未赋值的字段个数。 返回值为 0 表示没有将任何字段赋值。 如果在第一次读取之前到达字符串结尾,则返回EOF。
    如果buffer或format是NULL调用指针,无效参数处理程序,如中所述参数验证。 如果允许继续执行,则这些函数返回 -1 并将errno设置为EINVAL。
    成功则返回参数数目,失败则返回-1,错误原因存于errno中。
    经多次测试[来源请求],在linux系统中成功返回的是成功转换的值的个数

例如:
sscanf(“1 2 3”,"%d %d %d",buf1, buf2, buf3); 成功调用返回值为3,即buf1,buf2,buf3均成功转换。
sscanf(“1 2”,"%d %d %d",buf1, buf2, buf3); 成功调用返回值为2,即只有buf1,buf2成功转换。
(注意:此处buf均为地址)

  1. 使用实例
char buf[512];

(1)一般用法

sscanf("123456 ", "%s", buf);
printf("%s\n", buf);
结果为:123456

(2)取指定长度的字符串。如在下例中,取最大长度为4字节的字符串。

sscanf("123456 ", "%4s", buf);
printf("%s\n", buf);
结果为:1234

(3)取到指定字符为止的字符串。如在下例中,取遇到空格为止字符串。

sscanf("123456 abcdedf", "%[^ ]", buf);
printf("%s\n", buf);
结果为:123456

(4)取仅包含指定字符集的字符串。如在下例中,取仅包含1到9和小写字母的字符串。

sscanf("123456abcdedfBCDEF", "%[1-9a-z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf

(5) 取到指定字符集为止的字符串。如在下例中,取遇到大写字母为止的字符串。

sscanf("123456abcdedfBCDEF", "%[^A-Z]", buf);
printf("%s\n", buf);
结果为:123456abcdedf

(6)给定一个字符串iios/12DDWDFF@122,获取 / 和 @ 之间的字符串,先将 "iios/"过滤掉,再将非’@'的一串内容送到buf中

sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf);
printf("%s\n", buf);
结果为:12DDWDFF

(7)给定一个字符串"hello, world",仅保留"world"。(注意:“,”之后有一空格)

sscanf("hello, world", "%*s%s", buf);
printf("%s\n", buf);
结果为:world  

P.S. %*s表示第一个匹配到的%s被过滤掉,即hello被过滤了,
如果没有空格则结果为NULL。

2.1.1.4判断优先级

返回值:-1小于;0等于;1大于;2匹配错误
在这里插入图片描述
在这里插入图片描述
可参照: https://blog.csdn.net/weixin_44266050/article/details/102751739

int Priority(char s1,char s2)//返回操作符的优先级大小
//-1小于;0等于;1大于;2匹配错误
{
	if (s1 == '+' || s1 == '-'){
		if (s2 == '+' || s2 == '-' || s2 == ')' || s2 == '#')
			return 1;
		else
			return -1;
	}
	else if (s1 == '*' || s1 == '/'){
		if (s2 == '(')
			return -1;
		else
			return 1;
	}
	else if (s1 == '('){
		if (s2 == '+' || s2 == '-' || s2 == '*' || s2 == '/' || s2 == '(')
			return -1;
		else if (s2 == ')')
			return 0;
		else
			return 2;
	}
	else if (s1 == ')'){
		if (s2 == '(')
			return 2;
		else
			return 1;
	}
	else if (s1 == '#'){
		if (s2 == '#')
			return 0;
		else if (s2 == ')')
			return 2;
		else
			return -1;
	}
}
2.1.1.5基本运算
double Operate(double x, double y, char op)
{
	if (op == '+') return x + y;
	if (op == '-') return x - y;
	if (op == '*') return x*y;
	if (op == '/') return x / y;
	else return -1;
}

2.1.2AC代码

#include <iostream>
#include <cstdio>
#include <string.h>
#include <ctype.h>
using namespace std;

double stDit[300];//操作数栈(顺序结构)
char stOp[300], str[300];//操作符栈,输入的字符串
int top1, top2;//操作数栈的栈顶指针,操作符栈的栈顶指针(就是数组(栈)的下标)

int Priority(char s1,char s2)//返回操作符的优先级大小
//-1小于;0等于;1大于;2匹配错误
{
	if (s1 == '+' || s1 == '-'){
		if (s2 == '+' || s2 == '-' || s2 == ')' || s2 == '#')
			return 1;
		else
			return -1;
	}
	else if (s1 == '*' || s1 == '/'){
		if (s2 == '(')
			return -1;
		else
			return 1;
	}
	else if (s1 == '('){
		if (s2 == '+' || s2 == '-' || s2 == '*' || s2 == '/' || s2 == '(')
			return -1;
		else if (s2 == ')')
			return 0;
		else
			return 2;
	}
	else if (s1 == ')'){
		if (s2 == '(')
			return 2;
		else
			return 1;
	}
	else if (s1 == '#'){
		if (s2 == '#')
			return 0;
		else if (s2 == ')')
			return 2;
		else
			return -1;
	}
}
double Operate(double x, double y, char op)
{
	if (op == '+') return x + y;
	if (op == '-') return x - y;
	if (op == '*') return x*y;
	if (op == '/') return x / y;
	else return -1;
}

double Calc(bool &pipei)
{
	pipei = true;
	double x, y, tmp;
	char op;
	int i, n = strlen(str);

	top1 = top2 = -1;//栈顶为-1表示栈空(下标为0就是有一个栈内元素了)
	stOp[++top2] = '#';//操作符栈压入一个#界定符
	str[n++] = '#';//输入的字符串最后插上一个界定符(这样读取到最后和一开始压入的界定符就匹配成一对了)

	for (i = 0; i < n; ++i)
	{
		if (str[i] == ' ')
			continue;
		if (isdigit(str[i]))				//是数字
		{
			sscanf(str + i, "%lf", &tmp);//从子字符串中str+i开始读取一个为%lf型的值  ==(只要符合  能转化为%lf型  的条件就自动往下一个地址找直到不符合条件)== 赋给tmp
			stDit[++top1] = tmp;			//stack push
			//跳过后头的数字直到非数字
			while (isdigit(str[i + 1]))		//+1很重要
				++i;
		}
		else								//是操作符
		{
			int pri = Priority(stOp[top2],str[i]);
			if (pri == -1){
				stOp[++top2] = str[i];
			}
			if (pri == 1){
				y = stDit[top1--];//取栈顶操作数(x是前一个数,y在x后头)
				x = stDit[top1--];//取再前一个的操作数
				op = stOp[top2--];//取栈顶操作符
				stDit[++top1] = Operate(x, y, op);//计算结果入栈
				i--;//下一循环仍要比较这一字符
			}
			if (pri == 0){
				if (str[i] == ')'&&stOp[top2] == '(')
					top2--;
				else if (str[i] == '#'&&stOp[top2] == '#')
					return stDit[top1];
				else{
					pipei = false;
					return stDit[top1];
				}
			}
			else{
				pipei = false;
				return stDit[top1];
			}
		}
	}
	pipei = false;
	return stDit[top1];
}
int main()
{
	//循环运算并输出,输入在while中,每次循环先输入才会再判断
	while (gets(str) != NULL && strcmp(str, "0") != 0)//非空且非输入了0
	{
		bool pipei;
		double sum = Calc(pipei);
		if (pipei)
			printf("%.2lf\n", sum);
		else
			printf("匹配出错\n");
	}
	system("pause");
	return 0;
}

2.2后缀表达式的运算(最易计算,运算符顺序恰为运算顺序)

运算规则

逆波兰式的数基本在前,遇符运算

  1. 运算符在式中出现的顺序恰为表达式的运算顺序;
  2. 每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式。(前数在前,后数在后,例:x y /既是x/y

AC代码

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<ctype.h>
#include<algorithm>
#include<iostream>
using namespace std;

char str[1000], strop[400];//逆波兰字符串,操作符栈
double strnum[450];//操作数栈
int topop,topnum;//操作符,操作数栈的栈顶指针(指向栈顶元素,-1为栈空)
double op1, op2;//操作暂存数1,2

double OP(double x, double y, char t){
	if (t == '+')
		return (x + y);
	else if (t == '-')
		return (x - y);
	else if (t == '*')
		return (x*y);
	else if (t == '/')
		return (x / y);
}
double compute(){
	char t;//暂存符号
	topop = -1; topnum = -1;
	for (int i = 0; str[i] != NULL; i++){
		t = str[i];
		if (t == ' ')
			continue;
		else if (isdigit(t)){
			double tmp;
			sscanf(str + i, "%lf", &tmp);//注意这里的第二个参数不能加.*表示精确度,否则该函数无用
			strnum[++topnum] = tmp;
			while (isdigit(str[++i]));//跳过操作过的连续为数字的字符
			i--;//跳过一直到非数字字符,为了下次循环判断改数应先--抵掉for中的++
		}
		else{
			op2 = strnum[topnum--];
			op1 = strnum[topnum--];
			strnum[++topnum]=OP(op1, op2, t);
		}
	}
	return strnum[topnum];
}

int main(){
	cin.getline(str, sizeof(str));
	while (str != NULL&&strcmp(str, "0") != 0){//输入不空且不为0
		printf("%.2lf\n", compute());

		fill(str, str + strlen(str), 0);
		cin.getline(str, sizeof(str));
	}
	system("pause");
	return 0;
}

2.3前缀表达式的运算

运算规则

和后缀类似,顾名思义,波兰式逆转之后基本就是逆波兰式的运算
波兰式的数基本在后,所以逆转之后,遇符运算(两个操作数的顺序和逆波兰式相反

1.逆转表达式
2. 运算符在式中出现的顺序恰为表达式的运算顺序;
3. 每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式。(前数在后,后数在前,例:y x /既是x/y

AC代码

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<ctype.h>
#include<algorithm>
#include<iostream>
using namespace std;

char str[1000], strop[400];//逆波兰字符串,操作符栈
double strnum[450];//操作数栈
int topop, topnum;//操作符,操作数栈的栈顶指针(指向栈顶元素,-1为栈空)
double op1, op2;//操作暂存数1,2

double OP(double x, double y, char t){
	if (t == '+')
		return (x + y);
	else if (t == '-')
		return (x - y);
	else if (t == '*')
		return (x*y);
	else if (t == '/')
		return (x / y);
}
double compute(){
	reverse(str, str + strlen(str));

	char t;//暂存符号
	topop = -1; topnum = -1;
	for (int i = 0; str[i] != NULL; i++){
		t = str[i];
		if (t == ' ')
			continue;
		else if (isdigit(t)){
			double tmp;
			sscanf(str + i, "%lf", &tmp);//注意这里的第二个参数不能加.*表示精确度,否则该函数无用
			strnum[++topnum] = tmp;
			while (isdigit(str[++i]));//跳过操作过的连续为数字的字符
			i--;//跳过一直到非数字字符,为了下次循环判断改数应先--抵掉for中的++
		}
		else{
			op1 = strnum[topnum--];
			op2 = strnum[topnum--];
			strnum[++topnum] = OP(op1, op2, t);
		}
	}
	return strnum[topnum];
}

int main(){
	cin.getline(str, sizeof(str));
	while (str != NULL&&strcmp(str, "0") != 0){//输入不空且不为0
		printf("%.2lf\n", compute());

		fill(str, str + strlen(str), 0);
		cin.getline(str, sizeof(str));
	}
	system("pause");
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是利用栈将中缀表达式转换后缀表达式并进行运算的C语言代码: ```c #include <stdio.h> #include <stdlib.h> #include <ctype.h> typedef struct stack_node { char data; struct stack_node *next; } stack_node; typedef struct { stack_node *top; } stack; stack *create_stack() { stack *s = (stack *)malloc(sizeof(stack)); s->top = NULL; return s; } void push(stack *s, char c) { stack_node *node = (stack_node *)malloc(sizeof(stack_node)); node->data = c; node->next = s->top; s->top = node; } char pop(stack *s) { if (s->top == NULL) { printf("Error: stack is empty\n"); exit(1); } char c = s->top->data; stack_node *temp = s->top; s->top = s->top->next; free(temp); return c; } char peek(stack *s) { if (s->top == NULL) { printf("Error: stack is empty\n"); exit(1); } return s->top->data; } int is_empty(stack *s) { return (s->top == NULL); } int is_operator(char c) { return (c == '+' || c == '-' || c == '*' || c == '/'); } int precedence(char c) { switch (c) { case '+': case '-': return 1; case '*': case '/': return 2; default: return 0; } } char *infix_to_postfix(char *infix) { stack *s = create_stack(); char *postfix = (char *)malloc(sizeof(char) * (strlen(infix) + 1)); int i, j; for (i = 0, j = 0; infix[i] != '\0'; i++) { if (isdigit(infix[i])) { postfix[j++] = infix[i]; } else if (is_operator(infix[i])) { while (!is_empty(s) && is_operator(peek(s)) && precedence(peek(s)) >= precedence(infix[i])) { postfix[j++] = pop(s); } push(s, infix[i]); } else if (infix[i] == '(') { push(s, infix[i]); } else if (infix[i] == ')') { while (!is_empty(s) && peek(s) != '(') { postfix[j++] = pop(s); } if (is_empty(s)) { printf("Error: mismatched parentheses\n"); exit(1); } pop(s); } else { printf("Error: invalid character\n"); exit(1); } } while (!is_empty(s)) { if (peek(s) == '(') { printf("Error: mismatched parentheses\n"); exit(1); } postfix[j++] = pop(s); } postfix[j] = '\0'; return postfix; } int evaluate_postfix(char *postfix) { stack *s = create_stack(); int i, operand1, operand2, result; for (i = 0; postfix[i] != '\0'; i++) { if (isdigit(postfix[i])) { push(s, postfix[i] - '0'); } else if (is_operator(postfix[i])) { operand2 = pop(s); operand1 = pop(s); switch (postfix[i]) { case '+': result = operand1 + operand2; break; case '-': result = operand1 - operand2; break; case '*': result = operand1 * operand2; break; case '/': if (operand2 == 0) { printf("Error: division by zero\n"); exit(1); } result = operand1 / operand2; break; } push(s, result); } else { printf("Error: invalid character\n"); exit(1); } } result = pop(s); if (!is_empty(s)) { printf("Error: invalid expression\n"); exit(1); } return result; } int main() { char infix[100], *postfix; int result; printf("Enter an infix expression: "); scanf("%s", infix); postfix = infix_to_postfix(infix); printf("Postfix expression: %s\n", postfix); result = evaluate_postfix(postfix); printf("Result: %d\n", result); free(postfix); return 0; } ``` 该程序中,`stack_node`结构体表示栈中的节点,包含一个字符类型的数据`data`和一个指向下一个节点的指针`next`。`stack`结构体表示栈,包含一个指向栈顶节点的指针`top`。`create_stack`函数用于创建一个空栈,`push`函数用于将一个字符压入栈中,`pop`函数用于弹出栈顶字符,`peek`函数用于获取栈顶字符而不弹出,`is_empty`函数用于判断栈是否为空。 `is_operator`函数用于判断一个字符是否为运算符,`precedence`函数用于获取运算符的优先级。`infix_to_postfix`函数用于将中缀表达式转换后缀表达式,采用经典的算法:从左到右遍历中缀表达式,如果是数字直接输出到后缀表达式中,如果是左括号入栈,如果是右括号则弹出栈中的元素直到遇到左括号,如果是运算符则弹出栈中优先级大于等于它的元素并输出到后缀表达式中,然后将自己入栈。最后将栈中剩余的元素弹出并输出到后缀表达式中。在转换过程中需要处理一些错误情况,如不合法的字符、不匹配的括号等。 `evaluate_postfix`函数用于计算后缀表达式的值,也是采用经典的算法:从左到右遍历后缀表达式,如果是数字则入栈,如果是运算符则弹出栈中的两个操作数进行运算,并将结果入栈。最后栈中剩下的元素就是表达式的值。 在`main`函数中,首先读入中缀表达式,然后调用`infix_to_postfix`函数将其转换后缀表达式,并输出后缀表达式。接着调用`evaluate_postfix`函数计算后缀表达式的值,并输出结果。最后释放动态分配的内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Deosiree

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值