1. 程式人生 > >資料結構:簡單算數表示式二叉樹的構建和求值

資料結構:簡單算數表示式二叉樹的構建和求值

內容:編寫一個程式,先用二叉樹來表示一個簡單算術表示式,樹的每一個結點包括一個運算子或者運算數。在簡單算術表示式中只含+,-,*,/ 和一位正整數且格式正確(不包含括號),並且要先按照先乘除後加減的原則構造二叉樹,然後由對應的二叉樹計算該表示式的值。
解:

這裡用非遞迴演算法,根據對簡單算數表示式的觀察,如1+2*3-4/5  ,同一棵子樹中+、- 號一定在*,/ 號的上層,而且在表示式中如果直接單獨觀察+-的兩邊,如:1 + 2*3,2*3 - 4/5。會發現+-的兩側子樹要麼為數字節點,要麼為*/節點。於是不妨以加減法為分界線(因為會先計算乘除法)將表示式分割成若干個加減項後再將已生成的加減項節點(如2*3*4,8,3/2等)與加減符號項節點提取出來。則每個加減項節點(如2*3*4,8,3/2等)都是一棵棵小樹(子樹),將這些小樹

再由+-加減符號項為根節點依次連線成一棵"大樹"就構成了題目要求的二叉樹。

在生成小數的過程中,可由表示式字串,迴圈,逐字元讀取

若為數字

        新建節點p,將數字放入p->data,並且p左右孩子置空。

若為+-

        將前一個節點p放入事先準備好的一個存放加減項的陣列中,存放既證明這個加減項已經構造完畢,然後再將+-符號新建個節點放入另一個數組中。迴圈結束後依次將此兩個陣列中的節點相互串接起來形成二叉樹即可。

若為*,/

        則一定是構造成 數字 * 數字,的一棵小樹的形式,而前一個數字已經讀取,既為p節點,可由*/符號新建為根節點後,根節點的左孩子接上p,根節點的右孩子接上下一個字元的節點(下一個字元必為數字)。注意因為我們已經讀取了當前字元的下一個字元,則此時迴圈中逐字元讀取的計數器要+1一次,以防下次迴圈再次讀到這個字元。

然後,重點,將p指向此時的根節點(既字元為*/ 的節點),這樣進入下個迴圈後,若為*/,則可以將此p節點當做左子樹繼續接入*/為根的樹中。若為+-,則此p做為一個構造完整的加減項存入陣列。

最後將最後一個p放入加減項陣列即可。然後就是串接兩陣列節點的工作了。

具體程式碼如下(C語言)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef struct BTreeNode
{
	char data;
	struct BTreeNode* lchild;
	struct BTreeNode* rchild;
} BTNode;

int calcula(BTNode* T)
{
	if(T==NULL)
		return 0;
	if(T->data <='9'&&T->data >='0')
		return (T->data-'0');
	else
	{
		switch(T->data)            //因為這一步的T->data必為運算子,則必有左右孩子節點且不空
		{
     		case'+':  return calcula(T->lchild) + calcula(T->rchild); break;
			case'-':  return calcula(T->lchild) - calcula(T->rchild); break;
			case'*':  return calcula(T->lchild) * calcula(T->rchild); break;
			case'/':  return calcula(T->lchild) / calcula(T->rchild); break;
		}
	}
}

char* input_cheak_str()            //字串動態輸入與檢測函式
{
	printf("請輸入一個簡單算術表示式(一位正整數且只有+-*/無括號,輸入空格結束):\n");
	int ch_num=0;
	char ch,*str;
	str=(char*)malloc(sizeof(char));
	while((ch=getchar())!='\n')    //設定按照輸入字元數變化的字元陣列(記憶體足夠則不受陣列長度影響)
	{
		if(ch_num%2==1)            //下標為奇數,字元應為運算子號
		{
			if(ch!='+' && ch!='-' && ch!='*' && ch!='/')
			{
				printf("第%d個字元輸入不合法!應為“+-*/”之一",ch_num+1);
				return '\0';
			}
		}
		else                       //下標為偶數,字元應為數字
		{
			if(!(ch>='0' && ch<='9'))
			{
				printf("第%d個字元輸入不合法!應為0至9數字之一",ch_num+1);
				return '\0';
			}
		}
		str[ch_num]=ch;
		str=(char*)realloc(str,(++ch_num+1)*sizeof(char));   //∵ch_num為字元陣列下標,而realloc引數為字元個數
	}                                                        //∴新開陣列長度引數為下標+2,相當於引數為num++後的num+1
	if(str[ch_num-1]=='+' || str[ch_num-1]=='-' || str[ch_num-1]=='*' || str[ch_num-1]=='/')
	{                                                        //若最後一個字元為運算子則輸入不合法
		printf("最後一個字元輸入不合法!應為數字!",ch_num+1);
		return '\0';
	}
	str[ch_num]='\0';    //串結尾設定串結束符
	return str;
}

/*構建二叉樹演算法主體思路是將正確(輸入時經過檢測)的一位正整數無括號的簡單表示式字串,以加減法為分界線(因為會先計算乘除法)
將表示式分割成若干個加減項後再將已生成的加減項節點與加減符號項節點交替連線成樹,其中每個加減項節點都是其子樹。
*/

//本可以用陣列表示指標陣列ASItem,ASSign,因編譯器不支援C99故無法用變數定義陣列長,故只能用二級指標和malloc構造指標陣列
BTNode* creat_tree(char *str)
{
	int itemCount=0,ASCount=0,len=strlen(str),i; //AS意為addSub加減法,前者為加減項計數,後者為加減符號計數,用於陣列下標
	BTNode **ASItem,**ASSign,*root,*p;           //ASItem指標陣列存放加減項節點指標,ASSign指標陣列存放加減符號節點指標
	ASItem=(BTNode**)malloc((len/2+1)*sizeof(BTNode*));
	ASSign=(BTNode**)malloc((len/2)*sizeof(BTNode*)); 
	if(str[0]=='\0')                             //加減符號節點數必為加減項節點數+1.既itemCount==ASCount+1
		return NULL;
	for(i=0;i<len/2;i++)               //指標陣列置空
		ASSign[i]=NULL;
	for(i=0;i<len/2+1;i++)
		ASItem[i]=NULL;
	for(i=0;i<len;i++)                 //讀取str字元陣列
	{
		if(str[i]<='9' && str[i]>='0')
		{
			p=(BTNode*)malloc(sizeof(BTNode));
			p->data=str[i];
			p->lchild=p->rchild=NULL;
		}
		else if(str[i]=='+'||str[i]=='-')
		{
			ASItem[itemCount++]=p;     //將p節點放入加減項陣列
			p=(BTNode*)malloc(sizeof(BTNode));
			p->data=str[i];
			ASSign[ASCount++]=p;
		}   //將加減符號節點指標放入ASSign陣列,因有符號節點的孩子必不為空且建立過程不會訪問其孩子節點,故無需置空
		else                           //str[i]符號為乘除的情況
		{
			root=(BTNode*)malloc(sizeof(BTNode));
			root->data=str[i];         //將*,/作為資料存入根節點資料域
			root->lchild=p;            //p一定為數字或*,/節點(都是已構造好的)
			p=(BTNode*)malloc(sizeof(BTNode));
			p->data=str[++i];          //此時p為當前節點的下一個節點,此時str[++i]必為數字,且下一個訪問的str必為符號
			p->lchild=p->rchild=NULL;
			root->rchild=p;            //根節點的右孩子連上此節點
			p=root;                    //整個根節點構造完畢,傳入p
		}
	}
	ASItem[itemCount]=p;
	ASSign[0]->lchild=ASItem[0];       //第一個符號節點左孩子連第一個項節點
	ASSign[0]->rchild=ASItem[1];
	for(i=1;i<ASCount;i++)             //以加減法符號節點作為子樹根節點,加減法之間的項的節點為子樹根節點的孩子節點
	{                                  //加減符號節點數必為加減項節點數+1.既itemCount==ASCount+1,這裡構造時ASCount已自增一次
		ASSign[i]->lchild=ASSign[i-1]; //除第一個節點以外的加減符號節點左孩子都連上一個符號節點
		ASSign[i]->rchild=ASItem[i+1]; //右孩子都連項節點
	}
	return ASSign[ASCount-1];
}

int main ()
{
	printf("%d\n",calcula(creat_tree(input_cheak_str())));
	return 0;
}


執行結果(這裡由構造二叉樹,然後二叉樹計算直接列出出結果了,讀者可自行選擇新增輸出二叉樹函式)



相關推薦

資料結構簡單算數表示式構建

內容:編寫一個程式,先用二叉樹來表示一個簡單算術表示式,樹的每一個結點包括一個運算子或者運算數。在簡單算術表示式中只含+,-,*,/ 和一位正整數且格式正確(不包含括號),並且要先按照先乘除後加減的原則構造二叉樹,然後由對應的二叉樹計算該表示式的值。 解: 這裡用非遞迴演算

資料結構二分查詢與

關於二分查詢,原理其實不難,而且java Arrays類裡面有一個sorts()方法,可以先對資料進行排序,然後呼叫binarySerarch()方法,這個方法就是進行二分查詢用的。 下面是JDK的原始碼: private static int binarySe

軟考資料結構基礎——建立順序完全

  首先是關於樹,二叉樹,完全二叉樹的一些知識 一、樹     (一)、基本概念            1. 度:一個節點的子樹的個數    &

資料結構與演算法隨筆之------的遍歷(一文搞懂的四種遍歷)

二叉樹的遍歷 二叉樹的遍歷(traversing binary tree)是指從根結點出發,按照某種次序依次訪問二叉樹中所有的結點,使得每個結點被訪問依次且僅被訪問一次。 遍歷分為四種,前序遍歷,中序遍歷,後序遍歷及層序遍歷 前序 中

郝斌資料結構入門--P75-鏈式具體遍歷程式演示

郝斌資料結構入門--P75-鏈式二叉樹具體遍歷程式演示   知道遞迴的實現與應用,二叉樹的遍歷就容易了。   程式碼如下: #include <stdio.h> #include <malloc.h> struct BTNode {

資料結構實驗四順序表

#include<iostream> #include<string> #define MaxSize 100 using namespace std; class Tree {private: string array[100];

資料結構(四)之

二叉樹 二叉樹可以用陣列和鏈式結構這兩種方式來建立,這裡只介紹二叉樹的鏈式結構,並且實現二叉樹的前序、中序和後序遍歷。(運用二叉樹組定義靜態二叉樹的方式以註釋的形式寫明) 二叉樹的建立有三種方式:前序、中序和後序。這裡只展現了前序遍歷的方式。 #include<

資料結構與演算法11-線索

線索二叉樹 線索二叉樹的原理 我們把指向前驅和後繼的指標稱為線索,加上線索的二叉連結串列稱為線索,加上線索的二叉連結串列為線索連結串列,相應的二叉樹就稱為線索二叉樹(Threaded  Binary  Tree) lchild ltag

SDUTOJ1291資料結構上機測試4.1:的遍歷與應用1

以SDUTOJ1291資料結構上機測試4.1:二叉樹的遍歷與應用1為例 https://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Contest/contestproblem/cid/2711/pid/1291 思路: 遞迴實現,化解子問

資料結構知識整理 - 遍歷的應用

主要內容 建立二叉連結串列 複製二叉樹 計算二叉樹深度 統計二叉樹的結點個數   建立二叉連結串列 在先序遍歷的遞迴演算法中,將輸出語句改為輸入語句即可。(可回顧“遞迴演算法”) 需要注意的是,遞迴演算法會遍歷滿二叉樹中的每一個結點,

java資料結構與演算法之平衡(AVL)的設計與實現

關聯文章:   上一篇博文中,我們詳細地分析了樹的基本概念以及二叉查詢樹的實現過程,基於二叉查詢樹的特性,即對於樹種的每個結點T(T可能是父結點),它的左子樹中所有項的值小T中的值,而它的右子樹中所有項的值都大於T中的值。這意味著該樹所有的元素可以用某

資料結構-劍指offer-把列印成多行

題目:從上到下按層列印二叉樹,同一層結點從左至右輸出。每一層輸出一行。因為要按層列印,首先想到的是層次遍歷。在二叉樹的深度這道題中,首先應用到了層次遍歷。每一層的節點值存入一個小vector c,再把小vector c存到大vector vec中,列印vec。(題目沒有要求換

資料結構)第五章

二叉樹 資料結構大致分為兩種型別:基於陣列的實現和基於連結串列的實現。 兩種結構的特點: 基於陣列的實現:通過下標或秩,在常數時間內找到目標物件。插入和刪除,需要耗費線性時間。 基於連結串列的實現:藉助引用或位置物件,在常數時間內插入或刪除元素。但需要線性時間,對整個結構進行遍歷查詢

[資料結構]實驗五_順序

#include<iostream> using namespace std; class SeqBinaryList { public: //T init(); SeqBinaryList() {} //~SeqBinaryList(); void

資料結構-----後序遍歷非遞迴演算法(利用堆疊實現)

一、非遞迴後序遍歷演算法思想 後序遍歷的非遞迴演算法中節點的進棧次數是兩個,即每個節點都要進棧兩次,第二次退棧的時候才訪問節點。 第一次進棧時,在遍歷左子樹的過程中將"根"節點進棧,待左子樹訪問完後,回溯的節點退棧,即退出這個"根"節點,但不能立即訪問,只能藉助於這個"根"

資料結構 C語言 鏈式

【問題描述】 採用二叉連結串列作為二叉樹的儲存結構實現各項功能 【任務要求】 (1) 輸入二叉樹的先序序列,建立二叉樹; (2) 用程式實現二叉樹的中序遍歷; (3) 編寫程式求二叉

資料結構 BFS層次遍歷【C語言版本】

//案例輸入(其中的“#”表示空,並且輸入過程中不要加回車) 輸入序列ABC##DE#G##F### 輸入序列ABD##E##CF#G### 輸入序列ABD###C## #include <stdio.h> //測試OK,可以執行 #include <s

C++資料結構--按層次遍歷

class node {    public:       T val; //節點值 node<T>* left; //左節點  node<T>* right; //右節點 node():val(T()),left(nullptr),right(nullptr){} node(T v,

資料結構之---C語言實現的順序儲存

//二叉樹的順序儲存 //這裡利用迴圈佇列儲存資料 //楊鑫 #include <stdio.h> #include <math.h> #include <stdlib.h> #include <string.h> #defi

(C語言-資料結構與演算法)還原

/*根據先序和中序遍歷輸出的字串還原二叉樹,然後輸出後序遍歷*/ #include<stdio.h> #include<stdlib.h> #define MAX 20 char preOrder[MAX] = "abdgcefh";//定義全域性變數,先序字串 char midOrd