数据结构老师发了这么一道题:
将中缀表达式转换为后缀表达式
以下是c语言实现的中缀表达式代码(粘贴老师的)
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
#pragma region 使用粘贴符定义复用栈代码
#define DelcareStack(type) \
typedef struct type##_Node \
{ \
type data; \
struct type##_Node *next; \
} type##Stack; \
int type##StackEmpty(type##Stack *stack) \
{ \
return stack == NULL; \
} \
void type##StackPush(type##Stack **stack, type c) \
{ \
type##Stack *node = (type##Stack *)malloc(sizeof(type##Stack)); \
node->data = c; \
node->next = *stack; \
*stack = node; \
} \
int type##StackPop(type##Stack **stack, type *data) \
{ \
if (*stack == NULL) \
return 0; \
else \
{ \
*data = (*stack)->data; \
*stack = (*stack)->next; \
return 1; \
} \
} \
int type##StackTop(type##Stack *stack, type *data) \
{ \
if (stack == NULL) \
return 0; \
else \
{ \
*data = stack->data; \
return 1; \
} \
}
#pragma endregion 复用栈
DelcareStack(char)
DelcareStack(float)
int higherThan(char opInExp, char opInStack)
{
char *ops = "+-*/\%^";
int rate[6] = {0, 0, 1, 1, 1, 2};
int expRate, stackRate;
if (opInExp == ')')
return 0;
if (opInExp == '(')
return 1;
if (opInStack == '(')
return 1;
expRate = rate[(char *)strchr(ops, opInExp) - ops];
stackRate = rate[(char *)strchr(ops, opInStack) - ops];
return expRate - stackRate;
}
float calc(float a, float b, char op)
{
switch (op)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
case '%':
return (int)a % (int)b;
case '^':
return pow(a, b);
}
}
float calcExp(char *expression)
{
charStack *op=NULL; //运算符栈
floatStack *ob=NULL; //对象栈
char *p = expression, opInStack;
float a, b;
while (*p)
{
if (strchr("0123456789", *p))
{
floatStackPush(&ob, *p - 48);
p++;
}
else
{
if (charStackEmpty(op))
{
charStackPush(&op, *p);
p++;
}
else
{
charStackTop(op, &opInStack);
if (higherThan(*p, opInStack) > 0)
{
charStackPush(&op, *p);
p++;
}
else
{
charStackPop(&op, &opInStack);
if (opInStack != '(')
{
floatStackPop(&ob, &b);
floatStackPop(&ob, &a);
printf("calculating %f%c%f\n", a, opInStack, b);
floatStackPush(&ob, calc(a, b, opInStack));
}
else
p++;
}
}
}
}
while (!charStackEmpty(op))
{
floatStackPop(&ob, &b);
floatStackPop(&ob, &a);
charStackPop(&op, &opInStack);
printf("calculating %f%c%f\n", a, opInStack, b);
floatStackPush(&ob, calc(a, b, opInStack));
}
floatStackPop(&ob, &a);
return a;
}
int main()
{
printf("%.2f", calcExp("3*2^(4+2*2-1*3)-5"));
getchar();
}
什么是复用栈呢,在网上看到这么一句话
最开始写的栈,通过宏来改变元素数据类型,在同一程序中只能用于一种数据类型,想要用于多种数据类型则要复制代码并改名。那么,有没有方法不复制代码就可以用于多种数据类型?
源于:
weixin_30375247https://blog.csdn.net/weixin_30375247
说白了,使用复用栈是因为你不知道存储的数据类型是什么,比如一个公式1+1*2,里面既有整型数据,也有字符数据。
从理论上,如何将中缀表达式转换为后缀表达式?
以下是CSDN大佬的总结:
2.1.1 栈的应用-四则运算表达式求值规则
1.设定运算符栈;
2.从左到右遍历中缀表达式的每个数字和运算符;
3.若当前字符是数字,则直接输出成为后缀表达式的一部分;
4.若当前字符为运算符,则判断其与栈顶运算符的优先级,若优先级大于栈顶运算符,则进栈;若优先级小于栈顶运算符,退出栈顶运算符成为后缀表达式的一部分,然后将当前运算符放入栈中;
5.若当前字符为“(”,进栈;
6.若当前字符为“)”,则从栈顶起,依次将栈中运算符出栈成为后缀表达式的一部分,直到碰到“(”。将栈中“(”出栈,不需要成为后缀表达式的一部分,然后继续扫描表达式直到最终输出后缀表达式为止。
源于:
没错酷呆https://blog.csdn.net/qq_43290883由于博主实力有限,不能在老师的原基础上进行更改(换句话,老师写的代码我看不懂...)就只能自己另寻思路了(看的懂上面代码的大佬们可以教教我哈~Q2214891127)
Java代码实现如下:
import java.util.Scanner;
import java.util.Stack;
/**
* 中缀表达式转换为后缀表达式
* @Author Asunne
* @Date 2022/09/28
*/
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一段公式");
String str1 = sc.next(); // str1用来存储用于录入中缀表达式
String str2 = ""; // str2用来记录对应的后缀表达式
System.out.println(str1);
boolean flag = true; // 定义标识符,用来判断操作符的优先级
Stack<Character> st = new Stack<Character>(); // 定义存放Character类型的栈
int num = str1.length(); // 记录要循环的次数
int i=0; // i用来遍历str1
while((num--)>0)
{
if(str1.charAt(i)<='9'&&str1.charAt(i)>='0')
str2+=str1.charAt(i);
else if(str1.charAt(i)=='(')
st.push(str1.charAt(i));
else if(str1.charAt(i)==')')
{
while(st.peek()!='('){
str2+=st.pop(); // 取出括号之间的所有操作符
}
st.pop(); // 取出左括号
}
else {
// flag如果为true则意味着操作符的优先级高于栈顶操作符,应该压入栈中,否则一直取出栈顶元素
if(!st.empty())
flag = compare(st.peek(),str1.charAt(i));
while(flag == false && !st.empty())
{
str2 += st.pop();
if(st.empty())
flag = true;
else
flag = compare(st.peek(),str1.charAt(i));
}
st.push(str1.charAt(i));
}
System.out.println(str1.charAt(i));
if(!st.empty())
System.out.println(st.peek());
System.out.println(str2);
System.out.println("------------------");
i++;
}
while(!st.empty())
str2 += st.pop();
System.out.println("用户输入的中缀表达式:"+str1);
System.out.println("公式对应的后缀表达式:"+str2);
}
static boolean compare(Character a,Character b){
int k1,k2; // k1和k2分别用来记录a,b操作符对应的权值
// 使用Switch语句对k1和k2进行赋权
k1=k2=0;
switch (a){
case '(':
k1 = -1;
break;
case '+':
k1 = 1;
break;
case '-':
k1 = 1;
break;
case '*':
k1 = 2;
break;
case '/':
k1 = 2;
break;
case '^':
k1 = 3;
break;
}
switch (b){
case '+':
k2 = 1;
break;
case '-':
k2 = 1;
break;
case '*':
k2 = 2;
break;
case '/':
k2 = 2;
break;
case '^':
k2 = 3;
break;
} // 两个Switch语句显得代码有点重复,感兴趣的读者可以自行优化
return (k2>k1);
}
}
测试用例:(2*(9+6/3-5)+4)
测试结果:
C语言实现如下(有一点问题,呜呜呜~~~博主是个废物):
/*将中缀表达式转换为后缀表达式*/
/**
* Author: Asunne
* Date: 2022/09/28
**/
#include <stdio.h>
#include <stdlib.h>
typedef char ElementType; // 此方法定义数据类型,易于后续的维护
typedef enum
{
true,
false
} bool; // c语言没有自带的布尔值类型,需要自己定义
typedef struct Node
{
ElementType character[100];
int top; // 用于记录栈顶位置
} Stack; // 使用顺序栈实现需求
void transform(char str[]);
bool compare(char a, char b);
int main()
{
char str[10000];
gets(str);
transform(str);
}
void transform(char str[])
{
Stack S;
S.top = -1;
int i = -1; // i用来遍历输入的字符数组
int n = strlen(str); // 记录字符数组的总长度
bool flag; // 定义标识符
while (n--)
{
i++;
if (str[i] == "(")
S.character[++S.top] = str[i];
else if (str[i] <= '9' && str[i] >= '0')
printf("%c", str[i]); // 如果读到数字,直接输出
else if (str[i] == ')')
{
while (S.character[S.top] != '(')
{
printf("%c", S.character[S.top--]);
}
S.top--;
continue;
}
else
{
if(S.top == -1)
{
S.character[++S.top] = str[i];
continue;
}
while(flag == false)
{
printf("%c",S.character[S.top--]);
flag = compare(S.character[S.top],str[i]);
}
S.character[++S.top] = str[i];
}
}
while(S.top!=-1)
printf("%c",S.character[S.top--]);
}
bool compare(char a, char b)
{
int k1, k2; // k1和k2分别用来记录a,b操作符对应的权值
k1 = k2 = 0;
// 使用Switch语句对k1和k2进行赋权
switch (a)
{
case '(':
k1 = -1;
break;
case '+':
k1 = 1;
break;
case '-':
k1 = 1;
break;
case '*':
k1 = 2;
break;
case '/':
k1 = 2;
break;
case '^':
k1 = 3;
break;
}
switch (b)
{
case '+':
k2 = 1;
break;
case '-':
k2 = 1;
break;
case '*':
k2 = 2;
break;
case '/':
k2 = 2;
break;
case '^':
k2 = 3;
break;
} // 两个Switch语句显得代码有点重复,感兴趣的读者可以自行优化
if(k2 > k1)
return true;
return false;
}
测试样例:2+3*(7-4)+8/4
测试结果(这是个错误的结果,不管了):