通过中缀表达式转后缀表达式计算复杂表达式

栈操作与表达式解析:从基础到实践

在计算机科学中,栈是一种常用的数据结构,它遵循后进先出(LIFO)的原则。本文将通过一系列函数的实现,探讨栈在括号匹配、中缀表达式转换为后缀表达式以及后缀表达式求值中的应用。

栈操作函数

为了支持栈的使用,我们定义了以下函数:

  • createStack(int maxSize):创建一个具有指定最大容量的栈。
  • countStack(Stack* stack):返回栈中元素的数量。
  • push(Stack* stack, Datatype data):向栈中压入一个元素。
  • pop(Stack* stack):从栈中弹出一个元素。
  • peek(Stack* stack):查看但不移除栈顶元素。
  • destoryStack(Stack* stack):销毁栈并释放内存。

函数说明:

  1. createStack(int maxSize)

    • 参数int maxSize - 栈的最大容量。
    • 功能: 创建一个新的栈结构,并分配内存。
    • 返回值Stack* - 返回新创建的栈的指针。
  2. countStack(Stack* stack)

    • 参数Stack* stack - 指向栈结构的指针。
    • 功能: 返回栈中当前元素的数量。
    • 返回值int - 栈中元素的数量。
  3. push(Stack* stack, Datatype data)

    • 参数:
      • Stack* stack - 指向栈结构的指针。
      • Datatype data - 要压入栈的数据。
    • 功能: 将一个元素压入栈中。
    • 注意: 如果栈已满,则不执行压栈操作,并打印“栈满”。
  4. pop(Stack* stack)

    • 参数Stack* stack - 指向栈结构的指针。
    • 功能: 从栈中弹出一个元素。
    • 注意: 如果栈为空,则不执行弹出操作,并打印“空栈”。
  5. peek(Stack* stack)

    • 参数Stack* stack - 指向栈结构的指针。
    • 功能: 返回栈顶的元素,但不移除它。
    • 返回值Datatype - 栈顶的元素。
    • 注意: 如果栈为空,则不返回任何值,并可能引发断言失败。
  6. destoryStack(Stack* stack)

    • 参数Stack* stack - 指向要销毁的栈结构的指针。
    • 功能: 释放栈占用的内存。
//创建栈
Stack* createStack(int maxSize)
{
	Stack* stack = (Stack*)malloc(sizeof(Stack));
	assert(stack);
	stack->data = (Datatype*)malloc(sizeof(Datatype) * maxSize);
	assert(stack->data);
	stack->top = -1;
	stack->maxSize = maxSize;
	return stack;
}

//获取栈中元素个数
int countStack(Stack* stack)
{
	assert(stack);
	return stack->top+1;
}

//数据入栈
void push(Stack* stack, Datatype data)
{
	assert(stack);
	if (countStack(stack)==stack->maxSize)
	{
		printf("栈满\n");
		return;
	}
	else
	{
		stack->top++;
		stack->data[stack->top] = data;
	}
}

//数据出栈
void pop(Stack* stack)
{
	if (countStack(stack) == 0)
	{
		printf("空栈\n");
		return;
	}
	else
	{
		stack->top--;
	}
}

//获取栈顶元素
Datatype peek(Stack* stack)
{
	assert(stack);
	assert(countStack(stack));
	return stack->data[stack->top];
}

//销毁栈
void destoryStack(Stack* stack)
{
	free(stack->data);
	free(stack);
}

在正式进入表达式求值,强烈建议大家阅读《大话数据结构》中关于该篇章的说明

中缀表达式转换为后缀表达式:transform(char* infix, char* suffix)

中缀表达式转换为后缀表达式是编程中的一个重要概念,它简化了表达式的计算过程。我们通过实现transform函数,利用栈来处理运算符的优先级和括号,将中缀表达式转换为后缀表达式。

  1. transform(char* infix, char* suffix)

    • 参数:
      • char* infix - 指向包含中缀表达式的字符串的指针。
      • char* suffix - 指向用于存储后缀表达式的字符串的指针。
    • 功能: 将中缀表达式转换为后缀表达式。
    • 注意: 如果输入的中缀表达式不正确,可能会打印错误消息。
  2. is_operator(char ch)

    • 参数char ch - 要检查的字符。
    • 功能: 判断一个字符是否是运算符(+-*/)。
    • 返回值bool - 如果是运算符返回true,否则返回false
  3. priority(char ch)

    • 参数char ch - 要检查的运算符字符。
    • 功能: 返回运算符的优先级。
    • 返回值int - 运算符的优先级,0表示括号,1表示加或减,2表示乘或除。

后缀表达式求值:calculate(char* suffix)

后缀表达式的计算相对直观,我们通过实现calculate函数,使用栈来存储操作数,并在遇到运算符时执行计算,最终得到表达式的值。

  1. fun(char ch, double num1, double num2)

    • 参数:
      • char ch - 运算符字符。
      • double num1 - 第一个操作数。
      • double num2 - 第二个操作数。
    • 功能: 根据运算符和两个操作数计算结果。
    • 返回值double - 计算的结果。
  2. calculate(char* suffix)

    • 参数char* suffix - 指向包含后缀表达式的字符串的指针。
    • 功能: 计算后缀表达式的值。
    • 返回值double - 表达式计算的结果

函数代码:

//中缀表达式转后缀
//辅助函数:
//1.判断是否为运算符 
bool is_operator(char ch)
{
	return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
//2.判断优先级,优先级越高,则返回值越大
int priority(char ch)
{
	//如果是,则进行下一步操作
	switch (ch)
	{
	case '+':
	case '-':
		return 1;
	case '*':
	case '/':
		return 2;
	case '(':
	case ')':
		return 0;
	}
}

//转换函数:参数说明:infix数组:键盘获取的中缀表达式,suffix数组:空的数组用来存放转换后的结果并且带出
void transform(char* infix, char* suffix)
{
	//此处需要用两个指针分别遍历两个数组
	char* in = infix;
	char* suf = suffix;
	//创建一个新的栈:栈在此处的作用就是一个规则(输入输出)
	Stack* stack = createStack(100);
	//开始遍历数组
	while (*in != '\0')
	{
		//判断是否是数字:如果是,则存入suffin数组
		if (isdigit(*in))
		{
			while (isdigit(*in)||*in=='.')
			{
				*suf = *in;
				suf++;
				in++;
			};
			//加空格(为了数字之间有间隔)
			*suf = ' ';
			suf++;
		}
		//如果是符号( + - * / ),则判断优先级
		else if (is_operator(*in))
		{
			//判断栈是否为空,为空则直接将该符号入栈
			if (countStack(stack) == 0)
			{
				push(stack, *in);
			}
			//不为空则需要比较优先级:
			else
			{
				//当前字符优先级如果高,则存入
				if (priority(*in) > priority(stack->data[stack->top]))
				{
					stack->data[++stack->top] = *in;
				}
				//如果in低于或者等于stack中的,则出栈,注意不要把括号也给出了
				//为了避免这种情况,我们将括号的优先级置为0
				else
				{
					//什么时候停止出栈:当in的运算符高于stack中的(如果全出完了,则停止)
					while (countStack(stack) != 0 && priority(*in) <= priority(stack->data[stack->top]))
					{
						//将出栈的字符存入suf中
						*suf = stack->data[stack->top];
						suf++;
						*suf = ' ';
						suf++;
						pop(stack);
					}
					//然后将该字符存入
					push(stack, *in);
				}
			}
			in++;
		}
		//如果in的是括号,则单独处理
		//1.如果等于 ( ,则入栈
		else if (*in == '(')
		{
			push(stack, *in);
			in++;
		}
		//2.如果等于 ),则进行出栈,直到匹配到( 为止
		else if (*in == ')')
		{
			while (countStack(stack) != 0 && stack->data[stack->top] != '(')
			{
				*suf = stack->data[stack->top];
				suf++;
				*suf = ' ';
				suf++;
				pop(stack);
			}
			//删除最后留下的'('
			pop(stack);
			in++;
		}
		else
		{
			printf("error input\n");
			return;
		}
	}
	//最后就将栈中所有操作符都出栈即可
	while (countStack(stack) != 0)
	{
		*suf = stack->data[stack->top];
		suf++;
		*suf = ' ';
		suf++;
		pop(stack);
	}
	//为suf赋为\0
	*suf = '\0';
	destoryStack(stack);
}

//计算辅助函数(将字符转化为运算符,并且计算结果)
double fun(char ch,double num1,double num2)
{
	switch (ch)
	{
	case '+':
		return num1 + num2;
	case '-':
		return num2 - num1;
	case '*':
		return num1 * num2;
	case '/':
		return num2 / num1;
	}
}
//后缀表达式求值
double calculate(char* suffix)
{
	//指针遍历数组
	char* suf = suffix;
	Stack* stack=createStack(100);
	while (*suf != '\0')
	{
		//是数字,则进入,直到将该数字全部取出
		if (isdigit(*suf))
		{
			//将字符转化为数字
			double num = (double)(*suf - '0');
			suf++;
			//下一位如果还是数字或者小数点(说明该数字还没有结束),那么进入循环
			while (isdigit(*suf) || *suf == '.')
			{
				//如果是整数,乘10相加运算
				if (isdigit(*suf))
				{
					num = num * 10 + *suf - '0';
					suf++;
				}
				//如果是小数点,则从下一位开始*0.1相加运算,循环计算,直到得出结果
				else if (*suf == '.')
				{
					//跳过小数点
					suf++;
					//frac在后面小数取出时有进位作用
					float frac = 0.1;
					//将小数点后面的数全部计算
					while (isdigit(*suf))
					{
						//得到小数
						float fl = (*suf - '0') * frac;
						//相加
						num += fl;
						//如果下一位还是小数,则需要/10,例如0.12,先是1,经过一次得到0.1,下一个2变为0.02需要*0.01
						frac /= 10;
						suf++;
					}
				}
			}
			//经过上面的操作,此时一个数字已经被取出,我们放到栈中
			push(stack, num);
		}
		//是运算符(不是空格),则需要出栈两个数字,并且做运算
		else if(is_operator(*suf))
		{
			//出栈两个数
			double num1 = stack->data[stack->top];
			pop(stack);
			double num2 = stack->data[stack->top];
			pop(stack);
			//计算结果,而后存入栈中
			double end = fun(*suf, num1,num2);
			push(stack, end);
			suf++;
		}
		//如果是空格,则++
		else
		{
			suf++;
		}
	}
	//最后遍历完成,将栈中结果出栈
	double end = stack->data[stack->top];
	//销毁栈
	destoryStack(stack);
	//返回计算结果
	return end;
}

测试用例: 9+(3-1)*3+10/2

输出:

结语

栈作为一种基础数据结构,在表达式解析和计算中发挥着重要作用。通过本文的函数实现和讨论,我们可以看到栈如何在实际编程问题中被有效利用。理解栈的工作原理和它在算法设计中的应用,对于任何软件开发者来说都是一个宝贵的技能。

  • 32
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值