1. 程式人生 > >中綴表示式生成二叉樹並利用字尾表示式進行求值運算

中綴表示式生成二叉樹並利用字尾表示式進行求值運算

這次是某人的課程作業,題目不難,但是很複雜

原題如下

  以字元序列的形式從鍵盤輸入不含變數的整數算術四則表示式(可以有小括號);
  檢查表示式的正確性
  將其轉換為二叉樹的儲存表示;
  通過後序遍歷得到其後綴表示式序列,輸出該序列;
  利用字尾表示式進行求值運算,輸出求值結果

原本按照我的思路,先求字尾表示式在求值與生成樹,不過既然題目這麼說了,就不得不按照題目要求去做......

1.表示式的正確性

原本的思路是做正則表示式,在利用包或者是自己寫一個自動機去實現,不過發現這裡面的正則表示式有一點小複雜.後來在網上找到了遞迴下降分析法的思路,就正好最近也在寫編譯原理的題目,剛好把詞法分析器辦過來用.

2.二叉樹生成

二叉樹的生成和字尾表示式的求值其實是很相似的,都是利用兩個棧,反覆比較兩個棧的內容,進行求值,只是一個是顯示數值,另外一個這是構造節點.在資料結構的書中講述過後綴表示式的演算法,在這裡就不贅述了

最後提供

#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include<stack>
using namespace std;

enum Type
{
	Other, Add, Sub, Mul, Div, Num, LetfBra, RightBra
};

typedef pair<string, Type> Tocken;

vector<Tocken> word;

struct Node
{
	string oper;//運算元或運算子   
	Node *left;//左子樹   
	Node *right;//右子樹   
};

int idx = Other;
Type sym;
int err = 0; // 錯誤

void E();
void E1();
void T();
void T1();
void F();
void PutIn(string& expr);

bool isOper(char op)
{
	return op == '+' || op == '-' || op == '*' || op == '/' || op == '(' || op == ')';
}

//求運算子的優先順序   
int getOperPri(char op)
{
	switch (op)
	{
	case '(':
		return 1; break;
	case '+':
	case '-':
		return 2; break;
	case '*':
	case '/':
		return 3; break;
	default:
		return 0;
	}
}

//銷燬二叉樹   
void freeTree(Node*& p)
{
	if (p->left != NULL)
		freeTree(p->left);
	if (p->right != NULL)
		freeTree(p->right);
	delete(p);
}

//表示式生成二叉樹   
void generateTree(Node*& p, vector<Tocken> WordArray)
{
	stack <char> operStack;
	stack <Node*> dataStack;
	char c;
	
	vector<Tocken>::iterator tmpite = WordArray.begin();
	while (tmpite != WordArray.end() || operStack.size() != 0)
	{
		if (tmpite != WordArray.end() && tmpite->second == 5)//是運算數字,則進運算元的棧   
		{
			p = new Node;
			p->oper = tmpite->first;
			p->left = NULL;
			p->right = NULL;
			dataStack.push(p);
			tmpite++;

		}
		else
		{
			if (tmpite != WordArray.end()){
				switch (tmpite->first[0])
				{
				case '('://進棧   
					operStack.push('(');
					tmpite++;
					break;
				case ')'://脫括號   
					while (true)
					{
						c = operStack.top();
						operStack.pop();
						if (c == '(')
						{
							break;
						}
						p = new Node;
						p->oper = c;
						p->left = NULL;
						p->right = NULL;
						if (dataStack.size())
						{
							p->right = dataStack.top();
							dataStack.pop();
						}
						if (dataStack.size())
						{
							p->left = dataStack.top();
							dataStack.pop();
						}
						dataStack.push(p);
					}
					tmpite++;
					break;
				default:
					if (operStack.size() == 0 || getOperPri(operStack.top()) < getOperPri(tmpite->first[0]))
					{//進棧
						operStack.push(tmpite->first[0]);

						tmpite++;
					}
					else
					{//出棧   
						p = new Node();
						p->oper = operStack.top();
						p->left = NULL;
						p->right = NULL;
						if (dataStack.size())
						{
							p->right = dataStack.top();
							dataStack.pop();
						}
						if (dataStack.size())
						{
							p->left = dataStack.top();
							dataStack.pop();
						}
						dataStack.push(p);
						operStack.pop();
					}
					break;
				}
			}

			else{
				p = new Node();
				p->oper = operStack.top();
				p->left = NULL;
				p->right = NULL;
				if (dataStack.size())
				{
					p->right = dataStack.top();
					dataStack.pop();
				}
				if (dataStack.size())
				{
					p->left = dataStack.top();
					dataStack.pop();
				}
				dataStack.push(p);
				operStack.pop();
			}
		}
	}
	p = dataStack.top();
	dataStack.pop();
}

void PutIn(string& expr)
{
	cout << "輸入轉化的表示式\n";
	cin >> expr;
}

bool AfterTraversal(Node* p, vector<string>& After)
{
	if (NULL == p){
		return false;
	}
	AfterTraversal(p->left, After);
	AfterTraversal(p->right, After);
	cout << p->oper << " ";
	After.push_back(p->oper);
	return true;

}

/*--------------------------------詞法分析----------------------------*/
bool word_analysis(vector<Tocken>& word, const string expr)
{
	for (int i = 0; i<expr.length(); ++i)
	{
		// 如果是 + - * / ( )
		if (expr[i] == '(' || expr[i] == ')' || expr[i] == '+'
			|| expr[i] == '-' || expr[i] == '*' || expr[i] == '/')
		{
			string tmp;
			tmp.push_back(expr[i]);
			switch (expr[i])
			{
			case '+':
				word.push_back(make_pair(tmp, Add));
				break;
			case '-':
				word.push_back(make_pair(tmp, Sub));
				break;
			case '*':
				word.push_back(make_pair(tmp, Mul));
				break;
			case '/':
				word.push_back(make_pair(tmp, Div));
				break;
			case '(':
				word.push_back(make_pair(tmp, LetfBra));
				break;
			case ')':
				word.push_back(make_pair(tmp, RightBra));
				break;
			}
		}
		// 如果是數字開頭
		else if (expr[i] >= '1' && expr[i] <= '9')
		{
			string tmp;
			while (expr[i] >= '0' && expr[i] <= '9')
			{
				tmp.push_back(expr[i]);
				++i;
			}

			word.push_back(make_pair(tmp, Num));
			--i;
		}
		else
		{
			return false;
		}
	}
	return true;
}
/*--------------------------------語法分析----------------------------*/
// 讀下一單詞的種別編碼
void Next()
{
	if (idx < word.size())
		sym = word[idx++].second;
	else
		sym = Other;
}

// E → TE' 
void E()
{
	T();
	E1();
}

// E' → +TE' | -TE' | ε 
void E1()
{
	if (sym == 1)
	{
		Next();
		T();
		E1();
	}
	else if (sym == 2)
	{
		Next();
		T();
		E1();
	}
	else if (sym != 7 && sym != 0)
	{
		err = -1;
	}
}

// T → FT' 
void T()
{
	F();
	T1();
}

// T' → *FT' | /FT' | ε 
void T1()
{
	if (sym == 3)
	{
		Next();
		F();
		T1();
	}
	else if (sym == 4)
	{
		Next();
		F();
		T1();
	}
	else if (sym != 1 && sym != 2 && sym != 7 && sym != 0)
	{
		err = -1;
	}
}

// F → (E) | d
void F()
{
	if (sym == 5)
	{
		Next();
	}
	else if (sym == 6)
	{
		Next();
		E();
		if (sym == 7)
		{
			Next();
		}
		else
		{
			err = -1;
		}
	}
	else
	{
		err = -1;
	}
}


int prior(char c)
{
	switch (c)
	{
	case '+':
	case '-':
		return 1;
	case '*':
	case '/':
		return 2;
	default:
		return 0;
	}
}

bool isOperator(char c)
{
	switch (c)
	{
	case '+':
	case '-':
	case '*':
	case '/':
		return true;
	default:
		return false;
	}
}


// 從棧中連續彈出兩個運算元
void popTwoNumbers(stack<int>& s, int& first, int& second)
{
	first = s.top();
	s.pop();
	second = s.top();
	s.pop();
}

// 計算字尾表示式的值
int expCalculate(const vector<string>& postfix)
{
	int first, second;
	stack<int> s;
	for (int i = 0; i<postfix.size(); ++i)
	{
		string c = postfix[i];
		switch (c[0])
		{
		case '+':
			popTwoNumbers(s, first, second);
			s.push(second + first);
			break;
		case '-':
			popTwoNumbers(s, first, second);
			s.push(second - first);
			break;
		case '*':
			popTwoNumbers(s, first, second);
			s.push(second*first);
			break;
		case '/':
			popTwoNumbers(s, first, second);
			s.push(second / first);
			break;
		default:
			s.push(atoi(c.c_str()));
			break;
		}
	}
	int result = s.top();
	s.pop();
	return result;
}


int main()
{
	string expr;
	PutIn(expr);
	vector<string> After;
	Node* p = new Node();
	if (!word_analysis(word, expr)){
		cout << "輸入一個錯誤的表示式" << endl;
	}

	else
	{
		Next();
		E();
		if (sym == 0 && err == 0){  // 注意要判斷兩個條件
			cout << "表示式正確" << endl;
			
			generateTree(p,word);
			AfterTraversal(p, After);
			cout << endl;
			cout << "值為:"<< expCalculate(After);
		}

		else
			cout << "輸入錯誤的表示式" << endl;
	}



	return 0;
}

原始碼(ps.因為是按模組組合寫的,可能有一些函式會有重複,大家看的時候見諒)

相關推薦

中綴表示式生成利用字尾表示式進行運算

這次是某人的課程作業,題目不難,但是很複雜 原題如下   以字元序列的形式從鍵盤輸入不含變數的整數算術四則表示式(可以有小括號);   檢查表示式的正確性   將其轉換為二叉樹的儲存表示;   通過後序遍歷得到其後綴表示式序列,輸出該序列;   利用字尾表示式進行求值運算,

【資料結構學習筆記】——根據中綴表示式構建輸出

要求 輸入一箇中綴表示式,構造表示式樹,以文字方式輸出樹結構。 輸入:例如,輸入a+b+c*(d+e) 輸出:以縮排表示二叉樹的層次,左(根),右(葉),上(右子樹),下(左子樹) 分析 我們有兩個核心的問題需要解決,一是如何按照中綴表示式來

C++實現利用(前序和中序生成)以及(的鏡像)

lse pub 非遞歸 ace 方法 [] reorder spa push #include<iostream> #include<string.h> #include<stack> using namespace std; type

【資料結構週週練】015 利用遞迴演算法建立鏈式儲存的轉換左右孩子結點

一、前言 哈哈,今天就是程式設計師節啦,祝大家1024程式設計師節快樂。 今天要給大家分享的演算法是交換二叉樹是的左右孩子結點,比較簡單,需要建立一個結點用來暫存左孩子結點,下面給大家送上程式碼。 二、題目 將下圖用二叉樹存入,並交換二叉樹是的左右孩子結點。其中圓角矩形內為結點資

先序表示式恢復成計算--For初學者

思路是這樣的:首先,將先序表示式轉化成二叉樹,其次,用後序來遍歷二叉樹,最後,通過後序遍歷二叉樹的結果來計算最終結果。 那麼問題來了,為什麼我們要通過後序表示式來計算最終結果。這是因為後序表示式我們計算過,點選這裡,所以,我們先把後序表示式的程式碼放進.h的檔案

java建立遍歷

java在實現非遞迴先根,中根,後根遍歷時需要用到鏈棧Linkstack類,實現非遞迴層次遍歷需要用到鏈佇列Linkqueue類,前面文章已經實現。 下面實現二叉鏈式儲存結構 package practice3; public class bitreeNode { private

輸入前序遍歷,中序遍歷重建返回

function reConstructBinaryTree(pre, vin) { if(pre.length===0||!pre){ return; }

按層遍歷列印換行

package com.zyl.algorithm; import java.util.LinkedList; public class PrintBinaryTreeByFloor { publ

[原始碼和文件分享]構造遍歷

介紹 已知二叉樹的後序遍歷和中序遍歷序列,構造對應的二叉樹,並非遞迴前序遍歷該二叉樹。 1 解題思路 先建立一個結構體,結構體中包含資料域以及左孩子和右孩子的指標域。然後首先輸入中序遍歷和後序遍歷的陣列,再定義四個變數:il,ir,pl,pr即中序遍歷的左右端點和後序遍歷的左右端點。然後

javascript生成, 以及其前中後序遍歷

序言 最近看了些面試題, 發現大多數都會問一個問題就是JavaScript生成二叉樹, 本來想偷懶百度看看算了, 可是發現好多網站部落格的程式碼都是一樣的, 並且生成的還是平衡二叉樹………. 如果我不輸入數字你給我生成一個試試. so, 看起來只能自己搞一下了. 百度百科–平衡二

jS生成的遍歷,查詢以及插入

js遞迴,二叉樹的操作 //遞迴演算法n次冪 function foo(n) { if (n == 1) { return 1; } else { return n * foo(n - 1); } } //console.log(foo(3)

的簡單應用--表示式

表示式樹 算數表示式是分層的遞迴結構,一個運算子作用於相應的運算物件,其運算物件又可以是任意複雜的表示式。二叉樹的遞迴結構正好用來表示這種表示式。下面只討論二元表示式。 二元表示式可以很自然的聯絡到二叉樹:以基本運算物件作為葉節點中的資料;以運算子作為非葉節

已知一個按先序序列輸入的字元序列,如abc,,de,g,,f,,,(其中逗號表示空節點)。請建立按中序和後序方式遍歷,最後出葉子節點個數和深度。

這是一個標準的模板題 記下了就完事了! Input   輸入一個長度小於50個字元的字串。 Output 輸出共有4行: 第1行輸出中序遍歷序列; 第2行輸出後序遍歷序列; 第3行輸出葉子節點個數; 第4行輸出二叉樹深度。 Sample Input abc,,

Java 通過先序中序序列生成

題目   二叉樹的前序以及後續序列,以空格間隔每個元素,重構二叉樹,最後輸出二叉樹的三種遍歷方式的序列以驗證。   輸入:   1 2 3 4 5 6 7 8 9 10   3 2 5 4 1 7 8 6 10 9   輸出:   1,2,3,4,5,6,7,8,9,10  

已知的前序和中序序列,構建後序序列,java實現。

已知二叉樹的前序和中序序列,或者已知二叉樹的後序和中序序列,是能夠唯一確定一棵二叉樹的。但是如果僅知道二叉樹的前序和後序序列,一般是不能唯一確定一棵二叉樹的,但是可以分析有多少種可能的二叉樹,這個沒有具體研究,只知道節點少的情況還能湊合分析出來,但是節點多的情況下可能性太多

建立排序中序遍歷

分析:中序遍歷也叫中根遍歷,顧名思義是把根節點放在中間來遍歷,其遍歷順序為左子節點–>根節點–>右子節點。 方法一: #include<iostream> using nam

資料結構--非遞迴遍歷利用輔助棧)

#include "StdAfx.h" #include <stdio.h> #include <stdlib.h> /*非遞迴方法前序遍歷二叉樹,利用輔助棧(指標陣列實現)。由於對指標不是很熟悉,程式碼較為混亂,基本上實現遍歷的功能。

層序生成

//假設節點的元素值均為整數,空節點的值為0 //注意,每個 非空節點 的孩子 都要寫出,否則程式無法結束 //下圖的樹,輸入為1 2 3 0 4 5 0 0 0 0 0 ^Z(windows程式結束標誌) #include <stdio.h> #include

第六章作業1—--計算機17級 6-1 高度 (20 分)

6-1 求二叉樹高度 (20 分) 本題要求給定二叉樹的高度。 函式介面定義: int GetHeight( BinTree BT ); 其中BinTree結構定義如下: typedef struct TNode *Position; typedef P

3.6 在中找到累加和為指定的最長路徑長度

【題目】:   給定一棵二叉樹的頭節點head和一個32位整數sum,二叉樹節點值型別為整型,求累加和為sum的最長路徑長度。路徑是指從某個節點往下,每次最多選擇一個孩子節點或者不選所形成的的節點鏈   例如, 二叉樹如圖所示               -3           3   &