数据结构:递归建立树: 输出其波兰式和逆波兰式

    这是前些天的数据结构实验课考试一道题目,题目描述模糊,没有标准输入输出例子,规定时间没做出来,回来又自己捣鼓才捣鼓出来。没有用栈和其他的,就是递归建树。

    波兰式和逆波兰式就不用多说了吧?大家肯定都知道。(其实我不知道怎么直接转换,但是我知道建树,然后前序遍历就是波兰式,后序遍历就是逆波兰式)。

    程序基本是用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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章