在前面的文章中,使用了栈这一数据结构将通常使用的中缀表达式转换成了后缀表达式,并再一次使用栈来对后缀表达式求值,从而计算出了表达式的值.
现在使用树这一数据结构来将后缀表达式还原为中缀表达式.使用的是表达式树.表达式树是二叉树的一种,所谓二叉树,要么它为为空树,要么不为空树,并且每个节点最多有两个孩子.而表达式树则是二叉树的一种,它的叶节点全为表达式中的数字,其余节点是运算符,即加,减,乘,除的运算符号.如图所示,就是一棵表达式树:
它的特点显而易见.若我们对其进行先序遍历(preorder),将获得该表达式的前缀版本(前缀表达式):-+3*29/64;对其中序遍历(inorder),得到中缀表达式:3+2*9-6/4;对其后序遍历(postorder),得到后缀表达式:329*+64/-.
通过给出某个版本的表达式,可以构建一棵该表达式所对应的表达式树.以后缀表达式为例,树节点的结构如下:
1 struct EtreeNode 2 { 3 char value; //节点的值 4 EtreeNode *left; //指向左孩子 5 EtreeNode *right; //指向右孩子 6 };
构造一棵表达式树,需要用到栈.算法与后缀表达式求值的算法很相似.若输入是一个数字,创建一个节点指针,保存该数字,左右孩子指针皆为空(nullptr),将该节点指针压栈;若输入是运算符号,同样的申请空间建立节点指针保存它,再从栈中弹出两个节点指针作为该指针的左右孩子域.依次进行,直到输入完结,弹栈,得到的是对应的表达式树的根节点.
C++实现如下:
1 EtreeNode* creat_etree(const string &in) //构建表达式树 2 { 3 if (!isdigit(in.at(0))) //第一个输入若为运算符 4 { 5 try 6 { 7 ; 8 throw runtime_error("illegal expression"); 9 } 10 11 catch (runtime_error err) 12 { 13 cout << err.what() << endl; 14 exit(EXIT_FAILURE); 15 } //第一个输入若为运算符,抛出异常,程序结束运行 16 } 17 18 stack <EtreeNode*> ptrStack; //保存节点指针的栈 19 20 for (auto &i : in) //遍历整个输入字符串 21 { 22 if (isdigit(i)) //若为数字 23 { 24 EtreeNode *ptr = new EtreeNode; 25 ptr ->value = i; 26 ptr ->left = ptr ->right = nullptr; 27 ptrStack.push(ptr); //保存该数字并压栈 28 } 29 30 else //为运算符 31 { 32 EtreeNode *b = ptrStack.top(); 33 ptrStack.pop(); 34 35 EtreeNode *a = ptrStack.top(); 36 ptrStack.pop(); //弹出两个数字 37 38 EtreeNode *ptr = new EtreeNode; 39 40 ptr ->value = i; 41 ptr ->left = a; 42 ptr ->right = b; 43 ptrStack.push(ptr); //指针指向两个数字并压栈 44 } 45 } 46 47 EtreeNode *root = ptrStack.top(); //弹出根节点 48 ptrStack.pop(); 49 50 return root; 51 }
这样,表达式树构建完成,其根节点指针为root,通过其即可访问整课表达式树.需要什么版本的表达式,就对其进行相应的遍历.如进行中序遍历得到中缀表达式:
1 void inorder_traversal(EtreeNode *r) //递归进行中序遍历 2 { 3 if (r == nullptr) 4 return; 5 6 else 7 { 8 inorder_traversal(r ->left); //引用左子树 9 cout << r ->value; //引用根节点 10 inorder_traversal(r ->right); //引用右子树 11 } 12 }
这也是一种将后缀表达式转换为中缀表达式的方法.
本人原创,谢谢大家!转载请注明出处,谢谢合作!