20172304 實驗二報告

20172304 實驗二報告

  • 課程:《軟件結構與數據結構》
  • 班級: 1723
  • 姓名: 段志軒
  • 學號:20172304
  • 實驗教師:王志強
  • 助教:張師瑜&張之睿
  • 實驗日期:2018年11月5日-2018年11月12日
  • 必修選修: 必修

    實驗要求

    實驗一:實現二叉樹
    參考教材p212,完成鏈樹LinkedBinaryTree的實現(getRight,contains,toString,preorder,postorder)
    用JUnit或自己編寫驅動類對自己實現的LinkedBinaryTree進行測試,提交測試代碼運行截圖,要全屏,包含自己的學號信息
    課下把代碼推送到代碼託管平臺
    實驗二:中序先序序列構造二叉樹
    基於LinkedBinaryTree,實現基於(中序,先序)序列構造唯一一棵二㕚樹的功能,比如給出中序HDIBEMJNAFCKGL和後序ABDHIEJMNCFGKL,構造出附圖中的樹,用JUnit或自己編寫驅動類對自己實現的功能進行測試,提交測試代碼運行截圖,要全屏,包含自己的學號信息
    實驗三:決策樹
    自己設計並實現一顆決策樹,提交測試代碼運行截圖,要全屏,包含自己的學號信息,課下把代碼推送到代碼託管平臺
    實驗四:表達式樹
    輸入中綴表達式,使用樹將中綴表達式轉換爲後綴表達式,並輸出後綴表達式和計算結果(如果沒有用樹,則爲0分),提交測試代碼運行截圖,要全屏,包含自己的學號信息,課下把代碼推送到代碼託管平臺
    實驗五:二叉查找樹
    完成PP11.3,提交測試代碼運行截圖,要全屏,包含自己的學號信息,課下把代碼推送到代碼託管平臺
    實驗六 : 紅黑樹分析
    參考本博客:點擊進入對Java中的紅黑樹(TreeMap,HashMap)進行源碼分析,並在實驗報告中體現分析結果。

    實驗過程及實驗結果

    實驗一過程及結果:
    LinedBinaryTree碼雲鏈接
    測試類及測試結果

package week6.jsjf;
public class LinkedBinaryTreeTest {
    public static void main(String[] args) throws InterruptedException {
        LinkedBinaryTree c=new LinkedBinaryTree("b");LinkedBinaryTree b=new LinkedBinaryTree("c");LinkedBinaryTree a=new LinkedBinaryTree("Translation",c,b);
        LinkedBinaryTree e=new LinkedBinaryTree("F");
        LinkedBinaryTree f=new LinkedBinaryTree("g",a,e);
        System.out.println("二叉樹的toString方法");
        System.out.println(f.toString());
        System.out.println("二叉樹的先序遍歷");
        f.preorder(f.getRootNode());
        System.out.println();
        System.out.println("二叉樹的後序遍歷");
        f.postorder(f.getRootNode());
        System.out.println();
        System.out.println("二叉樹的中序遍歷");
        f.Inorder(f.getRootNode());
        System.out.println();
        System.out.println("二叉樹的層序遍歷");
        f.unrecursionlevelOreder(f.getRootNode());
        System.out.println();
        System.out.println( "查找是否包含a的結果:"+f.contains("Translation")+"二叉樹中的元素:"+"b, c, Translation, F, g");
        System.out.println("查找是否包含A的結果:"+f.contains("A")+"二叉樹中的元素:"+"b, c, Translation, F, g");
        System.out.println("getRight方法的檢驗,即輸出樹的根節點的右子樹");
        System.out.println(f.getRight().toString());
        System.out.println("getLeft方法的檢驗,即輸出樹的根節點的左子樹");
        System.out.println(f.getLeft().toString());
        System.out.println("二叉樹的高度:"+f.getHeight()+"二叉樹的元素個數:"+f.size()+"二叉樹的根元素"+f.getRootElement());

    }
}


實驗二
這個實驗要求的是通過給定的中綴表達式和後綴表達式構建二叉樹題目中給定了中序HDIBEMJNAFCKGL和先序ABDHIEJMNCFGKL。
我們由先序知道二叉樹的根節點是爲A,在中序遍歷中就可以知道以A爲根節點的左子樹爲“HDIBEMJN”,右子樹爲“FCKGL”。由於我是使用數組記錄的兩個序列。所以在每一次確定完根節點以後,我就會更新數組的指針,在通過遞歸依次確定左子樹的根節點和右子樹的根節點,在進行過幾次遞歸之後。就可以得到以中序遍歷以及先序遍歷爲基礎的二叉樹了。
測試結果

BTCreation碼雲鏈接

實驗三
決策樹,這個的實現是基於書上的代碼,這個實際上也沒有也沒有太大的難度
DecisionTree碼雲鏈接
CharacterAnalyzer
這是我設計的決策樹

測試結果

實驗四
這個實驗主要要求是通過二叉樹將中綴表達式轉爲後綴表達式,輸出並計算後綴表達式並計算後綴表達式的結果。
實驗分析:書上已經有了將後綴表達式構建二叉樹並計算的方法了,實際上餓哦們只需要將中綴表達式按照優先級的順序錄入二叉樹中,就可以調用樹中的方法計算並得到結果了。然後我就做了實際上原理與那個直接中綴轉後綴相仿。
碼雲鏈接
Translation
測試結果截圖

測試代碼

public static void main(String[] args) {
        Translation translation =new Translation();
        String a="8 / 4 - 9 + 11 * 2";
        translation.Translate(a);
        System.out.println("中綴表達式樹是");
        System.out.println(translation.getTree());
        System.out.println("後序遍歷是"+translation.PostOrder());

        System.out.println("結果是"+translation.getResult());


    }

實驗五
這個實驗主要就是測試一下沒有什麼好講的
碼雲鏈接
LinkedBinarySearchTree

測試代碼

package week7.jsjf;

public class LinkedBinarySearchTreeTest {
    public static void main(String[] args) throws InterruptedException {
        LinkedBinarySearchTree a=new LinkedBinarySearchTree();
        a.addElement(80);
        a.addElement(10);
        a.addElement(9);
        a.addElement(6);
        a.addElement(7);
        a.addElement(85);
        a.addElement(16);
        a.addElement(44);
        System.out.println(a.toString());
        System.out.println(a.findMax());
        System.out.println(a.findMin());
        System.out.println(a.find(85));
        a.removeElement(10);
        System.out.println(a.toString());
        a.removeMax();
        System.out.println(a.toString());
        a.removeMin();
        System.out.println(a.toString());
    }
}

測試結果

//Node是單向鏈表,它實現了Map.Entry接口
static class Node<k,v> implements Map.Entry<k,v> {
final int hash;
final K key;
V value;
Node<k,v> next;
//構造函數Hash值 鍵 值 下一個節點
Node(int hash, K key, V value, Node<k,v> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}

public final K getKey()        { return key; }
public final V getValue()      { return value; }
public final String toString() { return key + = + value; }

public final int hashCode() {
    return Objects.hashCode(key) ^ Objects.hashCode(value);
}

public final V setValue(V newValue) {
    V oldValue = value;
    value = newValue;
    return oldValue;
}
//判斷兩個node是否相等,若key和value都相等,返回true。可以與自身比較爲true
public final boolean equals(Object o) {
    if (o == this)
        return true;
    if (o instanceof Map.Entry) {
        Map.Entry<!--?,?--> e = (Map.Entry<!--?,?-->)o;
        if (Objects.equals(key, e.getKey()) &&
            Objects.equals(value, e.getValue()))
            return true;
    }
    return false;
}

}
實驗六
TreeMap

// 默認構造函數。使用該構造函數,TreeMap中的元素按照自然排序進行排列。
    TreeMap()

// 創建的TreeMap包含Map
    TreeMap(Map<? extends K, ? extends V> copyFrom)

// 指定Tree的比較器
    TreeMap(Comparator<? super K> comparator)

// 創建的TreeSet包含copyFrom
    TreeMap(SortedMap<K, ? extends V> copyFrom)
//構建一個具有默認初始容量 (16) 和默認加載因子 (0.75) 的空哈希映像
    HashMap()

//構建一個哈希映像,並且添加映像m的所有映射
    HashMap(Map<? extends K,? extends V> m)

//構建一個擁有特定容量的空的哈希映像
    HashMap(int initialCapacity)

//構建一個擁有特定容量和加載因子的空的哈希映像
    HashMap(int initialCapacity, float loadFactor)
  • TreeMap的Empty方法,firstEntry()和getFirstEntry()都是用於獲取第一個節點。但是,firstEntry() 是對外接口;getFirstEntry()是內部接口。而且,firstEntry()是通過getFirstEntry()來實現的。兩個方法並會顯得很麻煩,因爲通過firstEntry()方法和可以避免修改返回的Entry,這樣保證了完整性,而且產生了兩個方法,一個可以是避免修改的方法,一個可以是修改的方法。通過查閱資料可以總結出對firstEntry()返回的Entry對象只能進行getKey()、getValue()等讀取操作;而對getFirstEntry()返回的對象除了可以進行讀取操作之後,還可以通過setValue()修改值。
public Map.Entry<K,V> firstEntry() {
    return exportEntry(getFirstEntry());
}

final Entry<K,V> getFirstEntry() {
    Entry<K,V> p = root;
    if (p != null)
        while (p.left != null)
            p = p.left;
    return p;
} 
  • TreeMap的Key方法,返回大於/等於key的最小的鍵值對所對應的KEY,沒有的話返回null。
public K ceilingKey(K key) {
    return keyOrNull(getCeilingEntry(key));
}
  • TreeMap的values方法,values方法是通過new Values()來實現返回TreeMap中值的集合,而Values()正好是集合類Value的構造函數,這樣返回的是一個集合了。
public Collection<V> values() {
    Collection<V> vs = values;
    return (vs != null) ? vs : (values = new Values());
}
  • TreeMap的entrySet方法,entrySet方法是返回TreeMap的所有鍵值對組成的集合,而且它單位是單個鍵值對。
public Set<Map.Entry<K,V>> entrySet() {
    EntrySet es = entrySet;
    return (es != null) ? es : (entrySet = new EntrySet());
}
  • TreeMap還有兩個遍歷方法,順序遍歷和逆序遍歷,順序遍歷,就是從第一個元素開始,逐個向後遍歷;而倒序遍歷則恰恰相反,它是從最後一個元素開始,逐個往前遍歷。
  • TreeMap遍歷鍵值對的方法,先根據entrySet()獲取TreeMap的“鍵值對”的Set集合,再通過迭代器遍歷得到的集合。
Integer integ = null;
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
    Map.Entry entry = (Map.Entry)iter.next();
    key = (String)entry.getKey();
    integ = (Integer)entry.getValue();
}
  • TreeMap遍歷鍵的方法,先keySet()獲取TreeMap的鍵的Set集合,再通過迭代器遍歷得到的集合。
String key = null;
Integer integer = null;
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
    key = (String)iter.next();
    integer = (Integer)map.get(key);
}

HashMap
這個類我們已經很熟悉了,所謂哈希查找就是將所要存儲的數據通過一個公式來進行計算,得到所謂的“地址”,然後儲存在數組對應的“地址”中例如“5%4=1,8%4=0”,則“5”和“8”的地址分別爲“1“和”0”,但有時地址之間會發生衝突,這是就要通過其他方法來解決衝突,在就java自帶的HashMap中,解決這個衝突的方法是鏈地址法,也就是指在數組中存放的是鏈表,一旦存入的數據的地址發生衝突,那麼久將其插入鏈表之中,這樣就很好的解決了衝突的問題,在jdk1.8版本之前,解決衝突的首端都是通過鏈表,但是在jdk1.8.0中,解決這個問題的方案變成了兩個,就是在當鏈表中的元素小於或等於8個是,使用單鏈表來解決這個問題,當發生衝突的元素個數大於8個是,就使用一個紅黑樹來儲存這些元素,這樣可以提高效率。

HashMap儲存形式圖例

Node是HashMap的一個內部類,實現了Map.Entry接口,本質是就是一個映射(鍵值對)。下列代碼可以近似理解成類似於單鏈表的結點類

//Node是單向鏈表,它實現了Map.Entry接口
static class Node<k,v> implements Map.Entry<k,v> {
    final int hash;
    final K key;
    V value;
    Node<k,v> next;
    //構造函數Hash值 鍵 值 下一個節點
    Node(int hash, K key, V value, Node<k,v> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }
 
    public final K getKey()        { return key; }
    public final V getValue()      { return value; }
    public final String toString() { return key + = + value; }
 
    public final int hashCode() {
        return Objects.hashCode(key) ^ Objects.hashCode(value);
    }
 
    public final V setValue(V newValue) {
        V oldValue = value;
        value = newValue;
        return oldValue;
    }
    //判斷兩個node是否相等,若key和value都相等,返回true。可以與自身比較爲true
    public final boolean equals(Object o) {
        if (o == this)
            return true;
        if (o instanceof Map.Entry) {
            Map.Entry<!--?,?--> e = (Map.Entry<!--?,?-->)o;
            if (Objects.equals(key, e.getKey()) &&
                Objects.equals(value, e.getValue()))
                return true;
        }
        return false;
    }
}

這是紅黑樹的相關結點類

//紅黑樹
static final class TreeNode<k,v> extends LinkedHashMap.Entry<k,v> {
    TreeNode<k,v> parent;  // 父節點
    TreeNode<k,v> left; //左子樹
    TreeNode<k,v> right;//右子樹
    TreeNode<k,v> prev;    // needed to unlink next upon deletion
    boolean red;    //顏色屬性
    TreeNode(int hash, K key, V val, Node<k,v> next) {
        super(hash, key, val, next);
    }
 
    //返回當前節點的根節點
    final TreeNode<k,v> root() {
        for (TreeNode<k,v> r = this, p;;) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }
}

這是插入節點的方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 步驟①:tab爲空則創建 
    // table未初始化或者長度爲0,進行擴容
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // 步驟②:計算index,並對null做處理  
    // (n - 1) & hash 確定元素存放在哪個桶中,桶爲空,新生成結點放入桶中(此時,這個結點是放在數組中)
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    // 桶中已經存在元素
    else {
        Node<K,V> e; K k;
        // 步驟③:節點key存在,直接覆蓋value 
        // 比較桶中第一個元素(數組中的結點)的hash值相等,key相等
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
                // 將第一個元素賦值給e,用e來記錄
                e = p;
        // 步驟④:判斷該鏈爲紅黑樹 
        // hash值不相等,即key不相等;爲紅黑樹結點
        else if (p instanceof TreeNode)
            // 放入樹中
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        // 步驟⑤:該鏈爲鏈表 
        // 爲鏈表結點
        else {
            // 在鏈表最末插入結點
            for (int binCount = 0; ; ++binCount) {
                // 到達鏈表的尾部
                if ((e = p.next) == null) {
                    // 在尾部插入新結點
                    p.next = newNode(hash, key, value, null);
                    // 結點數量達到閾值,轉化爲紅黑樹
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    // 跳出循環
                    break;
                }
                // 判斷鏈表中結點的key值與插入的元素的key值是否相等
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    // 相等,跳出循環
                    break;
                // 用於遍歷桶中的鏈表,與前面的e = p.next組合,可以遍歷鏈表
                p = e;
            }
        }
        // 表示在桶中找到key值、hash值與插入元素相等的結點
        if (e != null) { 
            // 記錄e的value
            V oldValue = e.value;
            // onlyIfAbsent爲false或者舊值爲null
            if (!onlyIfAbsent || oldValue == null)
                //用新值替換舊值
                e.value = value;
            // 訪問後回調
            afterNodeAccess(e);
            // 返回舊值
            return oldValue;
        }
    }
    // 結構性修改
    ++modCount;
    // 步驟⑥:超過最大容量 就擴容 
    // 實際大小大於閾值則擴容
    if (++size > threshold)
        resize();
    // 插入後回調
    afterNodeInsertion(evict);
    return null;
}
平衡紅黑樹的代碼
static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root, TreeNode<K, V> x) {
        //將插入的節點塗成紅色
        x.red = true;
        //此時x節點是剛插入的節點
        // 這些變量名不是作者隨便定義的都是有意義的。
        // xp:x parent,代表x的父節點。
        // xpp:x parent parent,代表x的祖父節點
        // xppl:x parent parent left,代表x的祖父的左節點。
        // xppr:x parent parent right,代表x的祖父的右節點。
        for (TreeNode<K, V> xp, xpp, xppl, xppr;;) {
            //如果x.parent==null證明x是根節點,並將x(根節點)返回;平衡完畢。
            if ((xp = x.parent) == null) {
                //將根節點塗成黑色
                x.red = false;
                return x;
            //只要進入此if就不會滿足註釋中3條件的任意一個,直接將root返回;平衡完畢。
            } else if (!xp.red || (xpp = xp.parent) == null)
                return root;
            //若父節點是祖父節點的左子節點,與下面的完全相反,本質是一樣的
            if (xp == (xppl = xpp.left)) {
                //x節點的祖父節點的右子節點(叔叔節點)不爲null且是紅色,父節點必然也是紅色,此時滿足第1種情況。
                //操作:1.將祖父節點的右子節點(叔叔節點)、父節點塗爲黑色
                //     2.將祖父節點塗爲紅色
                //     3.將祖父節點賦給x(參照節點的變更)
                if ((xppr = xpp.right) != null && xppr.red) {
                    xppr.red = false;
                    xp.red = false;
                    xpp.red = true;
                    x = xpp;
                //進入else 說明已經是第2或第3種情況了    
                } else {
                    //第2種情況
                    // 操作:1.標記節點變爲x。
                    //        2.左旋
                    //      3.x的父節點、x的祖父節點隨之變化
                    if (x == xp.right) {
                        root = rotateLeft(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    //第3種情況 
                    //操作 1.將父節點塗黑
                    //    2.祖父節點塗紅
                    //    3.右旋
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateRight(root, xpp);
                        }
                    }
                }
            } else {//若父節點是祖父節點的右子節點,與上面的完全相反,本質一樣的
                if (xppl != null && xppl.red) {
                    xppl.red = false;
                    xp.red = false;
                    xpp.red = true;
                    x = xpp;
                } else {
                    if (x == xp.left) {
                        root = rotateRight(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateLeft(root, xpp);
                        }
                    }
                }
            }
        }
    }

擴展Hash表的代碼

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;//oldTab指向hash桶數組
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {//如果oldCap不爲空的話,就是hash桶數組不爲空
        if (oldCap >= MAXIMUM_CAPACITY) {//如果大於最大容量了,就賦值爲整數最大的閥值
            threshold = Integer.MAX_VALUE;
            return oldTab;//返回
        }//如果當前hash桶數組的長度在擴容後仍然小於最大容量 並且oldCap大於默認值16
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold 雙倍擴容閥值threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//新建hash桶數組
    table = newTab;//將新數組的值複製給舊的hash桶數組
    if (oldTab != null) {//進行擴容操作,複製Node對象值到新的hash桶數組
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {//如果舊的hash桶數組在j結點處不爲空,複製給e
                oldTab[j] = null;//將舊的hash桶數組在j結點處設置爲空,方便gc
                if (e.next == null)//如果e後面沒有Node結點
                    newTab[e.hash & (newCap - 1)] = e;//直接對e的hash值對新的數組長度求模獲得存儲位置
                else if (e instanceof TreeNode)//如果e是紅黑樹的類型,那麼添加到紅黑樹中
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;//將Node結點的next賦值給next
                        if ((e.hash & oldCap) == 0) {//如果結點e的hash值與原hash桶數組的長度作與運算爲0
                            if (loTail == null)//如果loTail爲null
                                loHead = e;//將e結點賦值給loHead
                            else
                                loTail.next = e;//否則將e賦值給loTail.next
                            loTail = e;//然後將e複製給loTail
                        }
                        else {//如果結點e的hash值與原hash桶數組的長度作與運算不爲0
                            if (hiTail == null)//如果hiTail爲null
                                hiHead = e;//將e賦值給hiHead
                            else
                                hiTail.next = e;//如果hiTail不爲空,將e複製給hiTail.next
                            hiTail = e;//將e複製個hiTail
                        }
                    } while ((e = next) != null);//直到e爲空
                    if (loTail != null) {//如果loTail不爲空
                        loTail.next = null;//將loTail.next設置爲空
                        newTab[j] = loHead;//將loHead賦值給新的hash桶數組[j]處
                    }
                    if (hiTail != null) {//如果hiTail不爲空
                        hiTail.next = null;//將hiTail.next賦值爲空
                        newTab[j + oldCap] = hiHead;//將hiHead賦值給新的hash桶數組[j+舊hash桶數組長度]
                    }
                }
            }
        }
    }
    return newTab;
}

代碼調試時遇見的問題

問題:怎麼用二叉樹將中綴表達式轉爲後綴表達式
解答:一開始真的沒有什麼思路。後來經過長時間的思考以及在諮詢過老師之後,大概知道了思路,具體思路就是將輸入的中綴表達式根據優先級的順序插入到樹中(以運算符爲根,然後優先級的優先的輸入到左子樹中)具體操作在註釋中

public Translation(){
            tree = new Stack<ExpressionTree>();
            stack = new Stack();
        }
        private ExpressionTree getOperand(Stack<ExpressionTree> treeExpression){
            ExpressionTree num;
            num = treeExpression.pop();
            return num;
        }
        public ExpressionTree Translate(String expression){
            ExpressionTree operand1,operand2;
            char operator;
            String tempToken;
            Scanner parser = new Scanner(expression);
            while(parser.hasNext()){
                tempToken = parser.next();//這個方法就是類似於StringTokenizer方法將一個字符串根據分割符分成不同的幾個字符塊
                operator=tempToken.charAt(0);//引用字符塊開頭的字節碼
                if ((operator == '+') || (operator == '-') || (operator=='*') || (operator == '/'))//判斷是否爲運算符{
                    if (stack.empty())
                        stack.push(tempToken);//當儲存符號的棧爲空時,直接進棧
                    else{
                        String a =stack.peek()+"";//因爲當ope.peek()='-'時,計算機認爲ope.peek()=='-'爲false,所以要轉化爲string 使用equals()方法
                        if (((a.equals("+"))||(a.equals("-")))&&((operator=='*')||(operator=='/')))//這是判斷優先級的方法。
                            stack.push(tempToken);//當得到的符號的優先級大於棧頂元素時,直接進棧
                        else {
                            String s = String.valueOf(stack.pop());
                            char temp = s.charAt(0);
                            operand1 = getOperand(tree);//這是將數字實例化成樹的類型
                            operand2 = getOperand(tree);//這是將數字實例化成樹的類型
                            tree.push(new ExpressionTree(new ExpressionTreeOp(1, temp, 0), operand2, operand1));//這是以運算符爲根節點,然後以運算數爲左右子節點構建二叉樹。
                            stack.push(operator);
                        }//當得到的符號的優先級小於棧頂元素或者優先級相同時時,數字棧出來兩個運算數,形成新的樹進棧
                    }
                }
                else
                    tree.push(new ExpressionTree(new ExpressionTreeOp(2,' ',Integer.parseInt(tempToken)), null, null));
            }
            while(!stack.empty()){
                String a = String.valueOf(stack.pop());
                operator = a.charAt(0);
                operand1 = getOperand(tree);//這是將數字實例化成樹的類型
                operand2 = getOperand(tree);//這是將數字實例化成樹的類型
                tree.push(new ExpressionTree(new ExpressionTreeOp(1, operator, 0), operand2, operand1));//這是以運算符爲根節點,然後以運算數爲左右子節點構建二叉樹。
            }
            return tree.peek();
        }

        public String getTree()
        {
            return (tree.peek()).printTree();
        }
    public int getResult(){
        return tree.peek().evaluateTree();
    }

        public String PostOrder(){
            Iterator iterator =  tree.peek().iteratorPostOrder();
            String result="";
            for (;iterator.hasNext();)
                result +=iterator.next()+" ";
            return result;
        }
    }

其他

 這次實驗難點在於實驗二和一些實驗四,即利用中序和後序遍歷來生成二叉樹,和使用二叉樹將中綴表達式轉爲後綴表達式,至於其他的實驗更加側重於練習。只有經過不斷地練習,熟悉,我們才能更加熟練的使用各種類和方法,才能夠更加創造性的進行開發和應用。

參考資料

1TreeMap
2HashMap

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