基本逻辑
二叉树有四种遍历方式:前序遍历、中序遍历、后续遍历、层序遍历,借用遍历方式就可以将一个表达式(中缀表达式)转换成不同形式,可问题的关键在于如何将中缀表达式构建成一颗二叉表达式树,并且通过后续遍历能够再次输出后缀表达式。
事实上,对于人而言,可以通过中缀表达式,轻松画出对应的二叉树(借用下面的原则),但想通过代码实现,则需要转换成后缀表达式,在通过后缀表达式构建出表达式树。
遵循一个原则:最后做的运算在最顶层
举个例子:
12+(5-3)/6-3*(4+7)
- 可以将它拆成两部分,12+(5-3)/6和3*(4+7),这里是将 - 作为最后做的运算,当然+也可以。-作为根结点
- 12+(5-3)/6也可以在分为12和(5-3)/6,+作为子树根结点;(5-3)/6,将/作为子树根结点,分成(5-3)和6两部分,最后5-3以-为根结点,5作为左孩子,3作为右孩子。
- 同理另一部分也是如此。
,

通过观察可见所有的数字都做的叶子
可忽略:
一开始我陷入一个误区,我认为可以直接借用二叉树将中缀表达式转换成后缀表达式,而实际上,还是需要用栈,先将中缀表达式转换成后缀表达式以后,再通过此时的后缀表达式构建表达式树,在通过后续遍历,遍历的结果同样也是后缀表达式,同样通过前序遍历,转换成前缀表达式,虽然可以通过此时的二叉树后续遍历计算出值,但这不是多此一举吗?直接利用栈就可以直接通过后缀表达式计算出表达式值。所以问题应该叫做:通过后缀表达式构建二叉树,后续遍历的方式输出。表达式
中缀表达式转换成后缀表达式的算法
infix已经存放好了中缀表达式字符串,遍历此字符串,遍历i为此字符串下标
postfix存放后缀表达式字符串,变量j为此字符串的下标
stack是一个栈
if 如果是数字,就存入postfix中
else if是‘( ’,入栈
else if是 ‘)’,需要将栈中,‘(’之前的所有运算符出栈,并存入postfix中(这里很容易忘记将‘(’出栈,不用存如postfix中)
else if是运算符(写一个判断字符的函数)
这里通过一个while语句,大致的实现即,满足栈不为空,栈顶运算符优先级大于此时的运算符,需要将栈中所有的优先级大于此时运算符的运算符出栈,但如果栈顶为‘(’就停止出栈,然后将此时的运算符重新入栈。
代码实现:
void infixToPostfix(char* infix,char* postfix)
{
char stack[100]
int top=-1;
int j=0;
for(int i=0;infix[i]!='\0';i++)
{
if(isdigit(infix[i]))//数字直接存入postfix中
{
postfix[j++]=infix[i]
}
else if(infix[i]=='(')//左括号直接入栈
stack[top++]=infix[i];
else if(infix[i]==')')//有括号需要将'('之前的所有元素弹出
{
while(top>=0&&stack[top]!='(')
{
postfix[j++]=stack[top--];
}
//注意还有一个'('需要弹出
if(stack[top]=='('&&top>=0)
{
top--;
}
}
//isOperater是判断运算符的一个函数
//precedence是判断优先级的函数,这里进行了省略
else if(isOperater(infix[i]))//判断运算符
{
//如果栈顶元素优先级大于此时的infix所指向的运算符,
//就需要将栈中的运算符弹出,直到栈空或者遇到'('
while(top>=0&&stack[top]!='('
&&precedence(stack[top])>precedence(infix[i]))
{
postfix[j++]=stack[top--];
}
stack[++top]=infix[i];
}
//弹出栈剩余所有的运算符
while(top>=0)
{
postfix[j++]=stack[top--];
}
postfix[j]='\0';
}
后缀表达式构建二叉树
// 从后缀表达式构建二叉表达式树
TreeNode* buildExpressionTree(char* postfix)
{
TreeNode* stack[100];
int top = -1;
for (int i = 0; postfix[i] != '\0'; i++)
{
if (isdigit(postfix[i]))
{
TreeNode* node = createNode(postfix[i]);
stack[++top] = node;
}
else if (isOperator(postfix[i]))
{
TreeNode* node = createNode(postfix[i]);
node->right = stack[top--];
node->left = stack[top--];
stack[++top] = node;
}
}
return stack[top];
}
后缀表达式构建二叉表达式的逻辑和直接采用后缀表达去计算值的思维是一样的:
遍历postfix,数字入栈,遇到运算符,就连续出栈两个元素,计算的值在重新入栈。
同理,在构建二叉树的时候,如果是数字,新建一个结点存该数字以后,入栈;
如果是运算符,就新建结点,以此运算符为子树根,出栈的元素分别存放到右左孩子中,并将此结点重新入栈。
如此反复,最终栈中只会剩下一个结点,且该结点为根。
最后只需要采用后续遍历二叉树,就可以计算出表达式的值了。
补充代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// 定义二叉树节点结构
typedef struct TreeNode {
char data;
struct TreeNode* left;
struct TreeNode* right;
} TreeNode;
// 创建新节点
TreeNode* createNode(char data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
// 判断字符是否为运算符
int isOperator(char ch) {
return (ch == '+' || ch == '-' || ch == '*' || ch == '/');
}
// 获取运算符优先级
int precedence(char ch) {
if (ch == '+' || ch == '-')
return 1;
if (ch == '*' || ch == '/')
return 2;
return 0;
}
上述代码中展现并不完善,对于动态内存使用完后还需要释放,以及主函数的相关内容并未展现,后续遍历的代码
1863

被折叠的 条评论
为什么被折叠?



