這是前些天的數據結構實驗課考試一道題目,題目描述模糊,沒有標準輸入輸出例子,規定時間沒做出來,回來又自己搗鼓才搗鼓出來。沒有用棧和其他的,就是遞歸建樹。
波蘭式和逆波蘭式就不用多說了吧?大家肯定都知道。(其實我不知道怎麼直接轉換,但是我知道建樹,然後前序遍歷就是波蘭式,後序遍歷就是逆波蘭式)。
程序基本是用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;
}