以下是笔者关于前中后缀表达式与二叉树相互转换的一次练习,其中最主要的是一个关于中缀表达式直接转树的算法,相比于用栈转后缀表达式再转二叉树,该算法基于四则运算的思路,很容易的就可以写出来,并且不用考虑括号的冗余和匹配的问题,可以支持多种二元运算,主要采用了分治递归思想,另附该算法的非递归实现。
#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));
}
};//所有成员方法都是静态的,相当于一个建表达式树的函数库