前中后缀表达式与表达式树
2015-06-25 17:35
501 查看
以下是笔者关于前中后缀表达式与二叉树相互转换的一次练习,其中最主要的是一个关于中缀表达式直接转树的算法,相比于用栈转后缀表达式再转二叉树,该算法基于四则运算的思路,很容易的就可以写出来,并且不用考虑括号的冗余和匹配的问题,可以支持多种二元运算,主要采用了分治递归思想,另附该算法的非递归实现。
[code]#include <string> #include <iostream> #include <iomanip> #include <stack> using namespace std; //表达式树的节点类 typedef struct _node_{ union{ char opt; double value; }; _node_ *left, *right; }node; //为了非递归实现中缀直接转树的算法,模拟系统调用栈的历史记录写的record struct record{ node *root; int left, right; record(node *root, int left, int right){ this->root = root; this->left = left; this->right = right; } }; //表达式树类 class exptree{ public: //运算符最高优先级 static const int max_priority = 20; //是运算符 static int is_operator(char c) { if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '^' || c == 'e' || c == 'E') return 1; return 0; } //返回运算符优先级 static int get_priority(char c) { switch (c) { case '+': case '-':return 1; case '*': case '/': case '%':return 2; case 'e': case 'E':return 3; case '^':return 4; default:return -1; } } //数字串转double static double get_double(char *mid, int left, int right) { int nfloat = right; double dtemp = 0; for (int i = left; i <= right; i++) { if (mid[i] == ' ')continue; if (mid[i] == '.'){ nfloat = i; continue; } dtemp = 10 * dtemp + mid[i] - '0'; }//求得itemp nfloat = right - nfloat; while (nfloat-- != 0)dtemp /= 10.0;//算得实际大小 return dtemp; } //返回a,b运算的结果 static double calculate(char c, double a, double b) { switch (c) { case '+':return a + b; case '-':return a - b; case '*':return a * b; case '/':return a / b; case '%':return (int)a % (int)b; case 'e': case 'E':return a*pow(10, b); default: break; } return -1; } //前缀建表达式树算法,root是表达式树根结点 static void pre_to_tree(char *pre, node *root) { int left, right;//数字串的左右边界 stack<node*> tstack; //前缀表达式转树,逆序建树 for (int pos = strlen(pre) - 1; pos > -1; pos--) { if (isdigit(pre[pos])) { right = pos;//数字串右端点 while (isdigit(pre[pos]) || pre[pos] == '.')pos--; left = pos + 1;//数字串左端点 node *child = new node(); child->value = get_double(pre, left, right); child->left = child->right = nullptr; tstack.push(child); }//数字节点入栈 if (is_operator(pre[pos])) { node *root = new node(); root->opt = pre[pos]; root->left = tstack.top(); tstack.pop(); root->right = tstack.top(); tstack.pop(); tstack.push(root); }//是运算符,出栈左右操作数,建新树 } root = tstack.top();//返回栈顶指针地址 }//前缀建树 //中缀直接转树的分治递归算法,mid是中缀表达式 //root是表达式树根,left,right是左右端点 static void mid_to_tree(char *mid, node *root, int left, int right) { int power = 1;//计算当前所在括号的层数,最外层为1 int position = -1;//最低优先级运算符的位置 //为寻找低优先级,运算符,需要设当前优先级为最高优先级 int priority = 0x0FFFFFFF; for (int pos = left; pos <= right; pos++) { if (mid[pos] == '(')power++;//括号内优先级高 if (mid[pos] == ')')power--;//括号外优先级低 if (is_operator(mid[pos])) { if (max_priority * power + get_priority(mid[pos]) <= priority) { priority = max_priority * power + get_priority(mid[pos]);//记录当前优先级 position = pos;//记录运算符位置 }//power + get_priority(mid[pos])为全局优先级 }//是运算符 }//寻找全局最低优先级运算符 if (position != -1) { root->opt = mid[position]; root->left = new node();//创建左子结点 root->right = new node();//创建右子节点 mid_to_tree(mid, root->left, left, position - 1);//递归左表达式 mid_to_tree(mid, root->right, position + 1, right);//递归右表达式 }//有运算符 else { //分段递归边界未考虑左右括号,需要处理数字段左右的括号空格 while (mid[left] == '(' || mid[left] == ' ')left++; while (mid[right] == ')' || mid[right] == ' ')right--; //根据数字串的左右边界计算数字串的值 root->value = get_double(mid, left, right); root->left = root->right = nullptr; }//无运算符,是数字 } //中缀表达式直接转树的非递归算法(非递归前序遍历二叉树) static void mid_to_tree(char *mid, node *root) { int count = 0; root = new node(); stack<record*> tstack; record *rec = new record(root, 0, strlen(mid) - 1); tstack.push(nullptr);//栈底监视哨 while (!tstack.empty()){ int power = 1;//计算当前所在括号的层数,最外层为1 int position = -1;//最低优先级运算符的位置 //为寻找低优先级,运算符,需要设当前优先级为最高优先级 int priority = INT_MAX; for (int pos = rec->left; pos <= rec->right; pos++) { if (mid[pos] == '(')power++;//括号内优先级高 if (mid[pos] == ')')power--;//括号外优先级低 if (is_operator(mid[pos])) { if (max_priority * power + get_priority(mid[pos]) <= priority) { //记录当前优先级 priority = max_priority * power + get_priority(mid[pos]); position = pos;//记录运算符位置 }//power + get_priority(mid[pos])为全局优先级 }//是运算符 }//寻找全局最低优先级运算符 if (position != -1) { //建立根结点 rec->root->opt = mid[position]; rec->root->left = new node(); rec->root->right = new node(); //右子树入栈 tstack.push(new record(rec->root->right, position + 1, rec->right)); //左子树下降 rec = new record(rec->root->left, rec->left, position - 1); }//有运算符,是新树根 else { //去掉数字两边的括号和空格 while (mid[rec->left] == '(' || mid[rec->left] == ' ')rec->left++; while (mid[rec->right] == ')' || mid[rec->right] == ' ')rec->right--; //取得数字段的值赋给root->value rec->root->value = get_double(mid, rec->left, rec->right); rec->root->left = rec->root->right = nullptr; //左子树访问完毕,转右子树 rec = tstack.top(); tstack.pop(); }//无运算符,是数字节点 } } //后缀建表达式树算法 static void pst_to_tree(char *pst, node *root) { int left, right;//数字串的左右边界 stack<node*> tstack;//表达式树栈 for (int pos = 0; pos < strlen(pst); pos++){ if (isdigit(pst[pos])){ node *child = new node(); child->value = 0; left = pos; while (isdigit(pst[++pos])); right = pos - 1; child->value = get_double(pst, left, right); child->left = child->right = nullptr; tstack.push(child); }//是数字,建立数字节点 if (is_operator(pst[pos])){ node *root = new node(); root->opt = pst[pos]; root->right = tstack.top(); tstack.pop(); root->left = tstack.top(); tstack.pop(); tstack.push(root); }//是运算符,出栈,建立子树压入栈 } root = tstack.top();//返回栈顶指针地址 }//后缀建树 //前中后序递归遍历表达式树 static void pre_traverse_tree(node *root, int pos) { if (root) { if (root->left&&root->right) cout << setw(pos * 5) << root->opt << endl; else cout << setw(pos * 5) << root->value << endl; pre_traverse_tree(root->left, pos + 1); pre_traverse_tree(root->right, pos + 1); } } static void mid_traverse_tree(node *root, int pos) { if (root) { mid_traverse_tree(root->left, pos + 1); if (root->left&&root->right) cout << setw(pos * 5) << root->opt << endl; else cout << setw(pos * 5) << root->value << endl; mid_traverse_tree(root->right, pos + 1); } } static void pst_traverse_tree(node *root, int pos) { if (root) { pst_traverse_tree(root->left, pos + 1); pst_traverse_tree(root->right, pos + 1); if (root->left&&root->right) cout << setw(pos * 5) << root->opt << endl; else cout << setw(pos * 5) << root->value << endl; } } //表达式树转前中后缀表达式 static void tree_to_pre(node *root) { if (root){ if (root->left&&root->right) cout << root->opt << " "; else cout << root->value << " "; tree_to_pre(root->left); tree_to_pre(root->right); } } static void tree_to_mid(node *root) { if (root) { if (root->left){ if (root->left->left&&root->left->right){ if (get_priority(root->opt) > get_priority(root->left->opt)){ cout << "("; } } }//左子树加左括号 tree_to_mid(root->left); if (root->left){ if (root->left->left&&root->left->right){ if (get_priority(root->opt) > get_priority(root->left->opt)){ cout << ")"; } } }//左子树加右括号 if (root->left&&root->right) cout << root->opt; else cout << root->value; if (root->right){ if (root->right->left&&root->right->right){ if (get_priority(root->opt) > get_priority(root->right->opt)){ cout << "("; } } }//右子树加左括号 tree_to_mid(root->right); if (root->right){ if (root->right->left&&root->right->right){ if (get_priority(root->opt) > get_priority(root->right->opt)){ cout << ")"; } } }//右子树加右括号 } } static void tree_to_pst(node *root) { if (root) { tree_to_pst(root->left); tree_to_pst(root->right); if (root->left&&root->right) cout << root->opt << " "; else cout << root->value << " "; } } //计算表达式值 static double calculate(node *root) { if (!root->left&&!root->right)return root->value; return calculate(root->opt, calculate(root->left), calculate(root->right)); } };//所有成员方法都是静态的,相当于一个建表达式树的函数库
相关文章推荐
- 和学生探讨吉林大学python问题
- 远程连接MySQL慢的原因及解决
- JavaScript学习笔记(一)
- IOS开发之手势——UIGestureRecognizer 共存
- TCP慢启动、拥塞避免、快速重传、快速回复
- java学习之旅18、19--switch语句_基本用法_case穿透现象,jdk7.0新特性_增强switch语句
- javascript深入理解js闭包
- From MSI to WiX, Part 1 - Required properties, by Alex Shevchuk
- 关于com组件的方法,以AE的IFieldsEdit为例
- Behavioral模式之Memento模式
- android使用ImageView加载本地SdCard图片和加载网络图片
- 聊聊高并发(三十八)解析java.util.concurrent各个组件(十四) 理解Executor接口的设计
- 几个有用的svn命令
- [LeetCode] Minimum Size Subarray Sum
- Tomcat:bio nio 的设计
- android如何实现两层可滚动view,内层优先响应
- initWithFrame 和 initWithCoder
- Python之dir()与__dict__的区别
- JSTL 的forEach标签循环迭代集合并逆序输出
- Django+MySQL安装配置详解(Linux)[更新为1.8.2版]