《數據結構》實驗二報告
學號: XXXXXXXXX |
機器號 10-413 |
姓名: xxx |
日期: 2020-11-18 |
程序名: Test.java |
|
實驗內容: 二叉樹的遍歷 |
一、目的和要求(需求分析):
1、掌握二叉樹的存儲結構以及二叉樹的建立和操作。
2、輸入一串表達式后,建立二叉樹,並對其進行先序、中序和后序的遍歷。
(輸入表達式如此形式:a+b*c-d-e/f….;以#號結束。)
3、遞歸實現表達式運算。
二、程序設計的基本思想,原理和算法描述:
1.程序結構:
主程序:Main函數
結構體-二叉樹節點:class BTree_Node
結構體-二叉樹樹結構:class BTree
遞歸構造二叉樹表達式:class BTree_Node init
釋放二叉樹節點:void destory_Tree
先序遍歷:void preshow
中序遍歷:void midshow
后序遍歷:void posshow
Java直接計算表達式:void Output_1
遞歸輸出表達式的值:double Output_2
2.數據結構:
本程序使用java語言來實現二叉樹表達式的構造以及輸出,首先創建輸入表達式字符串,經過init函數遞歸構造表達式二叉樹,將中綴表達式先替換為后綴表達式,並對+-*/符號進行手動的優先級判斷。最后使用前序,中序,后序遍歷輸出。最后使用輸出方法1:java中與js互相應用的ScriptEngineManager直接計算表達式的值,輸出方法2:遞歸方式計算表達式樹。
public static BTree_Node init(String str,int star,int end) {//遞歸構造樹 int num_oper=0; BTree_Node bnNode=new BTree_Node(null); BTree_Node p=new BTree_Node(null); int k; int pos=0; //核心代碼,遞歸時優先遞歸*/號,之后再+-號 //pos代表遞歸當前位置 if(star==end) {//遞歸結束條件 char ch=str.charAt(star); bnNode.data=ch+""; //System.out.println(bnNode.data); bnNode.lTree=bnNode.rTree=null; return bnNode; } else { //下面就是遞歸判斷條件 //i!=j:查找運算符 for(k=star;k<=end;k++) { char ch1=str.charAt(k); if(ch1=='+'||ch1=='-') { num_oper++; pos=k;//記錄位置 } } if(num_oper==0) {//沒有+- for(k=star;k<=end;k++) { char ch2=str.charAt(k); if(ch2=='*'||ch2=='/') { num_oper++; pos=k; } } } if(num_oper!=0) {//遞歸構造左右子樹 p.data=str.charAt(pos)+""; p.lTree=init(str, star, pos-1); p.rTree=init(str, pos+1, end); }else { return null; } } //System.out.println(p.data); return p; } 3.輸入輸出設計: 輸入: Scanner input_1=new Scanner(System.in);//輸入字符串 String string=input_1.next(); char ending=string.charAt(string.length()-1); while(ending!='#') { string+=input_1.next(); ending=string.charAt(string.length()-1); } string=string.substring(0, string.length()-1);//到這里才結束輸入字符串 輸出: private static void preshow(BTree_Node root) {//先序遍歷 // TODO Auto-generated method stub if(root!=null) { System.out.print(root.getData()+" "); preshow(root.getlTree()); preshow(root.getrTree()); } } private static void midshow(BTree_Node root) {//中序遍歷 // TODO Auto-generated method stub if(root!=null) { midshow(root.getlTree()); System.out.print(root.getData()+" "); midshow(root.getrTree()); } } private static void posshow(BTree_Node root) {//后序遍歷 // TODO Auto-generated method stub if(root!=null) { posshow(root.getlTree()); posshow(root.getrTree()); System.out.print(root.getData()+" "); } }
三、調試和運行程序過程中產生的問題及采取的措施:
1.遞歸構造二叉樹尋找結束遞歸條件以及遞歸條件的判斷有很大的十五
解決方法:多次進行條件嘗試,對於每一個部分都使用臨時輸出和斷點的方法來測試,並且結合百度以及課本的知識解決。而且對於二叉表達式,首先就是按照從左到右依次尋找最右面的+-*/號,然后遞歸從該位置開始的左右遞歸,從而達到構造的作用
3.遞歸方法計算表達式樹的時候計算錯誤,遞歸算法沒有計算好
解決方法:1.使用java特有的包直接計算字符串的值
2.先畫圖畫出來整個二叉樹的結構,然后再寫上去
2.中序表達式很難直接構造表達式二叉樹
解決方法:先轉換為后綴表達式,然后通過后綴構造就好了
四、源程序及注釋:
Test.java:
package com.atmyhome; import java.util.ArrayList; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.util.Scanner; public class Test { static public class BTree_Node{//節點 private String data;//值 private BTree_Node lTree;//左子樹 private BTree_Node rTree;//右子樹 public BTree_Node(String data) { this.data=data; } public String getData() { return data; } public BTree_Node getlTree() { return lTree; } public BTree_Node getrTree() { return rTree; } } static public class BTree{//二叉樹 private BTree_Node rootNode=new BTree_Node(null); ArrayList<BTree_Node> number=new ArrayList<>(); ArrayList<BTree_Node> operations=new ArrayList<>();//本來是想儲存運算符和數字的,最后好像不需要。。 public BTree_Node getrootNode() { return rootNode; } } public static BTree_Node init(String str,int star,int end) {//遞歸構造樹 int num_oper=0; BTree_Node bnNode=new BTree_Node(null); BTree_Node p=new BTree_Node(null); int k; int pos=0; //核心代碼,遞歸時優先尋找遞歸+-號,之后再*/號 //pos代表遞歸當前位置 if(star==end) {//遞歸結束條件 char ch=str.charAt(star); bnNode.data=ch+""; //System.out.println(bnNode.data); bnNode.lTree=bnNode.rTree=null; return bnNode; } else { //下面就是遞歸判斷條件 //i!=j:查找運算符 for(k=star;k<=end;k++) { char ch1=str.charAt(k); if(ch1=='+'||ch1=='-') { num_oper++; pos=k;//記錄位置 } } if(num_oper==0) {//沒有+- for(k=star;k<=end;k++) { char ch2=str.charAt(k); if(ch2=='*'||ch2=='/') { num_oper++; pos=k; } } } if(num_oper!=0) {//遞歸構造左右子樹 p.data=str.charAt(pos)+""; p.lTree=init(str, star, pos-1); p.rTree=init(str, pos+1, end); }else { return null; } } //System.out.println(p.data); return p; } public static void show(BTree_Node root) {//遍歷 System.out.println("先序遞歸遍歷"); preshow(root); System.out.println(""); System.out.println("中序遞歸遍歷"); midshow(root); System.out.println(""); System.out.println("后序遞歸遍歷"); posshow(root); System.out.println(""); } public void destory_Tree(BTree_Node root){//釋放內存 if(root!=null) { destory_Tree(root.lTree); destory_Tree(root.rTree); root=null; } } private static void preshow(BTree_Node root) {//先序遍歷 // TODO Auto-generated method stub if(root!=null) { System.out.print(root.getData()+" "); preshow(root.getlTree()); preshow(root.getrTree()); } } private static void midshow(BTree_Node root) {//中序遍歷 // TODO Auto-generated method stub if(root!=null) { midshow(root.getlTree()); System.out.print(root.getData()+" "); midshow(root.getrTree()); } } private static void posshow(BTree_Node root) {//后序遍歷 // TODO Auto-generated method stub if(root!=null) { posshow(root.getlTree()); posshow(root.getrTree()); System.out.print(root.getData()+" "); } } private static void Output_1(String string) {//java版本的直接對字符串表達式計算 // TODO Auto-generated method stub ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("nashorn"); String expression = string; try { String result = String.valueOf(scriptEngine.eval(expression)); System.out.println("Java特有對表達式直接計算"+result); } catch (ScriptException e) { e.printStackTrace(); } } //遞歸方式計算表達式樹 public static double Output_2 (BTree_Node root) { if(!(root.data.equals("+") || root.data.equals("-")||root.data.equals("*")||root.data.equals("/"))) return Double.parseDouble(root.data); else if(root.data.equals("+"))//加法 { return Output_2(root.lTree) + Output_2(root.rTree); } else if(root.data.equals("-"))//減法 { return Output_2(root.lTree) - Output_2(root.rTree); } else if(root.data.equals("*"))//乘法 { return Output_2(root.lTree) * Output_2(root.rTree); } else if(root.data.equals("/"))//除法 { return Output_2(root.lTree) / Output_2(root.rTree); } else { return 0; } } public static void main(String[] args) { BTree bTree=new BTree();//創建二叉樹 BTree_Node node=new BTree_Node(null); Scanner input_1=new Scanner(System.in);//輸入字符串 String string=input_1.next(); char ending=string.charAt(string.length()-1); while(ending!='#') { string+=input_1.next(); ending=string.charAt(string.length()-1); } string=string.substring(0, string.length()-1);//到這里才結束輸入字符串 node=init(string,0,string.length()-1);//這里為了方便就直接寫表達式了 show(node); System.out.println("輸出結果:"); Output_1(string);//輸出方法1: System.out.println("遞歸方法計算"+Output_2(node));//輸出方法2: } }
五、運行輸出結果:
六、心得與體會:
心得:
由於本次沒有上機給老師查看代碼,所以我這個實驗亮點就寫在這里面了。本次實驗我使用java做的,首先是我構造表達式二叉樹的時候,是使用遞歸方法來構造表達式二叉樹的,不是先轉化為后綴表達式再進行構造,而是先去找到表達式中最后一個運算符號,然后依次向表達式前后,也就是樹的上下進行遞歸構造,好吧,本質上都差不多。同時遞歸過程中也可以很清楚的看到每個節點及其父子樹的位置關系。
另外再說一下遍歷吧,遍歷都是差不多的,我這里也是使用遞歸遍歷,但是我在網上還尋找到了java與js互相調用的特殊函數ScriptEngine,可以直接根據輸入的表達式進行計算,這個我是感覺是更加方便的。
最后說一下,我這個程序雖說基本功能都可以實現,但是沒有考慮到括號的影響,下次我會多多查詢課本和網上資料來填補細節上的缺陷。謝謝。
體會:
掌握二叉樹的存儲結構以及二叉樹的建立和操作,對二叉樹的輸入和輸出有了更加深入的體驗,輸入一串表達式后,建立二叉樹,並對其進行先序、中序和后序的遍歷。並且了解並深入學習了遞歸實現表達式運算的方法。並且也通過這次實驗更加深入了解JAVA語言的魅力。