數據結構:遞歸建立樹: 輸出其波蘭式和逆波蘭式

    這是前些天的數據結構實驗課考試一道題目,題目描述模糊,沒有標準輸入輸出例子,規定時間沒做出來,回來又自己搗鼓才搗鼓出來。沒有用棧和其他的,就是遞歸建樹。

    波蘭式和逆波蘭式就不用多說了吧?大家肯定都知道。(其實我不知道怎麼直接轉換,但是我知道建樹,然後前序遍歷就是波蘭式,後序遍歷就是逆波蘭式)。

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

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