这是前些天的数据结构实验课考试一道题目,题目描述模糊,没有标准输入输出例子,规定时间没做出来,回来又自己捣鼓才捣鼓出来。没有用栈和其他的,就是递归建树。
波兰式和逆波兰式就不用多说了吧?大家肯定都知道。(其实我不知道怎么直接转换,但是我知道建树,然后前序遍历就是波兰式,后序遍历就是逆波兰式)。
程序基本是用C语言写的,当然C++的一点实用工具和特性也用了(如引用,如string类)。后面会贴出自己的代码,一百来行。代麻可能还有很多问题,
算法:通过构建一棵由表达式形成的树,前序遍历就是波兰式,后序遍历就是逆波兰式。
构造方法和相关函数:
输入一个表达式,保存在s串中。
- 造树Function(递归):
若i == j 则指向的是个数字,直接插入树。
若i、j指向括号和反括号则判断并进行处理(是否跳过括号)。
每一次在s串指定范围[ i , j ]内找到运算优先级最小的运算符w,将它加入到树的节点,然后递归的执行以s串中
[ i , w-1 ]的部分,递归的执行以s串中[ w+1 , j ]的一部分。
s串
执行过程(按照递归顺序)
- Find函数:
传入s串,以及 i 和 j ,在[ i , j ]内找到运算符优先级最小的那个,返回其在s串中的下标。程序中只要调用Find则必定能够找到并返回下标。
注意:如果在找寻的过程中碰到内部括号(即边界 i 和 j 指向的是【相对应的】括号) ,则把括号及其内部的东西都当成一个运算数。
- compar函数:
单纯比较两个传来的运算符的优先级,* > + 返回 1。
执行结果:
具体代码:
#include<iostream>
#include <string>
#define N 4 // + - * / 四种运算
using namespace std;
typedef char ElementType; //值类型
typedef struct BTreeNode { //树的节点
ElementType data;
struct BTreeNode *lchild;
struct BTreeNode *rchild;
}*BTree;
int mao[N][N] = {//四种运算的优先级:依次是 + - * / (e.g. *>+ so mao[2][0]=1)
{ 1,1,0,0 },
{ 1,1,0,0 },
{ 1,1,1,1 },
{ 1,1,1,1 },
};
char maoo[N] = { '+', '-', '*', '/' };
int compar(char a, char b) {//对比两种运算符优先级,若a>b则返回1否则返回0
int x, y;
for (int i = 0; i < N; i++) {
if (a == maoo[i])
x = i;
if (b == maoo[i])
y = i;
}
return mao[x][y];
}
int Find(const string &s, int a, int b) {//找到在s串中指定区间[a,b]内优先级最小的运算符的下标,做树的节点。且传入的区间不能是括号括住的,当然在CreateTree已经排除掉了(但是内部可以有括号,只是两个边界a、b不能为一个[对应]的括号)
char minc = '*'; //一开始假设的,后面的步骤会替换掉这个
int tip = -1; //一开始下标,找不到就返回-1,找到会替换
int bracket;
for (int i = a; i <= b; i++) {
bracket = 0;
if (s[i] == '(') { //指定区间内的括号例如"(a+b/(d+c))"全部看成一个[运算数],不看它内部的东西(即跳过),内部的东西在树的后序递归”分叉“会处理
++bracket;
while (bracket) {
++i;
if (s[i] == '(')
++bracket;
else if (s[i] == ')')
--bracket;
}
continue; //作用于外for循环
}
if ((s[i] > 'z' || s[i] < 'a') && compar(minc, s[i])) {
minc = s[i];//找到最小优先级,且不能是运算数(字母)
tip = i;//在s串中下标
}
}
return tip;//返回[a,b]区间最小优先级运算符,在s串里的下标
}
void CreateTree(BTree &t, const string &s, int a, int b) {
t = new struct BTreeNode;//申请节点
int bracket = 0; //括号标记判断
t->lchild = t->rchild = NULL;
if (a == b) {//此是递归结束标志:即指向一个字符(此字符必定是操作数)直接插入树就好。
t->data = s[a];
return;
}
if (s[a] == '(' && s[b] == ')') {//剥下最外层括号(***)-> ***
++bracket;
int j = a;
while (bracket) {//如果是(***)则处理,如果是(*)*(*)则不管,因为最左右的括号不是[对应]的,Find函数能处理他们。其中星号*可以是括号
++j;
if (s[j] == '(')
++bracket;
else if (s[j] == ')')
--bracket;
}
if (j == b)//剥下最外层括号
++a, --b; //e.g.括号(a+b)->a+b
}
int w = Find(s, a, b);//此时传入的a、b必定是有效的:不会是(****),可以是{ (***)*(***)或**(**)**......内部的括号当成一个运算数,上文已说明 },也就是说里面的元素都是简单运算符(+-*/)和运算数。总之,Find必须肯定能够返回一个简单运算符在s串中的下标
t->data = s[w];
CreateTree(t->lchild, s, a, w - 1);
CreateTree(t->rchild, s, w + 1, b);
}
void Travel1(const BTree &t) {//前序遍历
if (t) {
cout << t->data << ' ';
Travel1(t->lchild);
Travel1(t->rchild);
}
}
void Travel2(const BTree &t) {//中序遍历
if (t) {
Travel2(t->lchild);
cout << t->data << ' ';
Travel2(t->rchild);
}
}
void Travel3(const BTree &t) {//后序遍历
if (t) {
Travel3(t->lchild);
Travel3(t->rchild);
cout << t->data << ' ';
}
}
int main() {
BTree t;
string s;
cout << "input:" << endl;
cin >> s;//输入式子 e.g. a+b*(c+a-d/e)
CreateTree(t, s, 0, s.size() - 1);//一开始的区间就是0~N-1, 造出一棵树
cout << "波兰式: ";
Travel1(t);
cout << endl;
cout << "中序遍历: ";
Travel2(t);
cout << endl;
cout << "逆波兰式: ";
Travel3(t);
cout << endl;
system("pause");
return 0;
}