20162303 實驗二 樹

北京電子科技學院(BESTI)

實 驗 報 告

課程:程序設計與數據結構
班級: 1623
姓名: 石亞鑫
學號:20162303javascript

成績: 2分
指導教師:婁嘉鵬 王志強
實驗日期:10月23日html

實驗密級: 非密級
預習程度: 已預習
實驗時間:15:25-17:15java

必修/選修: 必修
實驗序號: cs_03node

實驗內容

  1. 完成鏈樹LinkedBinaryTree的實現(getRight,contains,toString,preorder,postorder)
    用JUnit或本身編寫驅動類對本身實現的LinkedBinaryTree進行測試,提交測試代碼運行截圖數組

  2. 基於LinkedBinaryTree,實現基於(中序,先序)序列構造惟一一棵二㕚樹的功能,好比教材P372,給出HDIBEMJNAFCKGL和ABDHIEJMNCFGKL,構造出附圖中的樹
    用JUnit或本身編寫驅動類對本身實現的功能進行測試,提交測試代碼運行截圖數據結構

  3. 完成PP16.6
    提交測試代碼運行截圖,要全屏,包含本身的學號信息
    課下把代碼推送到代碼託管平臺源碼分析

  4. 完成PP16.8
    提交測試代碼運行截圖,要全屏,包含本身的學號信息
    課下把代碼推送到代碼託管平臺post

  5. 完成PP17.1
    提交測試代碼運行截圖,要全屏,包含本身的學號信息
    課下把代碼推送到代碼託管平臺學習

6.參考http://www.cnblogs.com/rocedu/p/7483915.html對Java中的紅黑樹(TreeMap,HashMap)進行源碼分析,並在實驗報告中體現分析結果測試

實驗步驟

(1) 實驗一

public String toString() {
        String result = "";
        ArrayIterator <T> iter = new ArrayIterator <T> ();
        root.postorder ( iter );
        return result+iter;
    }

    public Iterator<T> preorder() {
        ArrayIterator <T> iter = new ArrayIterator <T> ();
        if (root != null)
            root.preorder ( iter );
        return  iter;
    }

    public Iterator<T> postorder() {
        ArrayIterator <T> iter = new ArrayIterator <T> ();
        if (root != null)
            root.postorder ( iter );
        return  iter;
    }

前序和後序與中序比較類似,調換一下順序就能夠了,toString原本應該按照層序遍歷打印,可是層序遍歷我暫時沒法實現,因而就先打印後序。

(2) 實驗二

(3) 實驗三

public class TwentyQuestions {
    private LinkedBinaryTree<String> tree;
    public TwentyQuestions()
    {
        String e1 = "你以爲本身可以吃苦嗎?";
        String e2 = "你以爲本身有耐心嗎?";
        String e3 = "你有遠大的目標嗎?";
        String e4 = "你喜歡鑽研嗎?";
        String e5 = "但願你用你的耐心專一於一件事情並最終完成。";
        String e6 = "你以爲你的目標容易實現嗎?";
        String e7 = "你有短時間目標嗎?";
        String e8 = "若是你擁有這些,那麼好好堅持,吃得苦中苦,方爲人上人。";
        String e9 = "實現目標刻苦鑽研是必選的,加油!";
        String e10 = "一個好的目標是須要盡本身的努力來實現的,若是目標能垂手可得的實現,那絕對不是一個好的目標。";
        String e11 = "既然目標不是那麼容易實現,那麼你更應該努力實現,而不是自暴自棄。";
        String e12 = "你擁有短時間目標卻不願吃苦實現它,那麼你的短時間目標只是空話。";
        String e13 = "你有夢想嗎?";
        String e14 = "有夢想是好事,可是毫不能空想,要爲實現目標付出努力。";
        String e15 = "你願意作出一些改變嗎?";
        String e16 = "那麼從如今開始改變,確立一個小目標並努力實現,一步一個腳印,加油!";
        String e17 = "你以爲一我的的一輩子必定要作成某一件事情嗎?";
        String e18 = "既然你心中依然有着想法,那麼但願你能爲此改變,無論何時,只要想改就不會晚。";
        String e19 = "有的時候平平淡淡也算一種生活方式,只要回首往事時可以對得起本身的心裏就能夠了。";
        LinkedBinaryTree<String> n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14,n15,n16,n17,n18,n19;
        n18 = new LinkedBinaryTree<String>(e18);
        n19 = new LinkedBinaryTree<String>(e19);
        n17 = new LinkedBinaryTree<String>(e17, n18, n19);

        n16 = new LinkedBinaryTree<String>(e16);
        n15 = new LinkedBinaryTree<String>(e15, n16, n17);

        n14 = new LinkedBinaryTree<String>(e14);
        n13 = new LinkedBinaryTree<String>(e13, n14, n15);

        n12 = new LinkedBinaryTree<String>(e12);
        n7 = new LinkedBinaryTree<String>(e7, n12, n13);

        n8 = new LinkedBinaryTree<String>(e8);
        n9 = new LinkedBinaryTree<String>(e9);
        n4 = new LinkedBinaryTree<String>(e4, n8, n9);

        n10 = new LinkedBinaryTree<String>(e10);
        n11 = new LinkedBinaryTree<String>(e11);
        n6 = new LinkedBinaryTree<String>(e6, n10, n11);

        n5 = new LinkedBinaryTree<String>(e5);
        n2 = new LinkedBinaryTree<String>(e2, n4, n5);
        n3 = new LinkedBinaryTree<String>(e3, n6, n7);
        tree = new LinkedBinaryTree<String>(e1, n2, n3);
    }
    public void diagnose()
    {
        Scanner scan = new Scanner(System.in);
        BinaryTree<String> current = tree;

        System.out.println ("你要根據現實狀況作出選擇:");
        while (current.size() > 1)
        {
            System.out.println (current.getRootElement());
            if (scan.nextLine().equalsIgnoreCase("Y"))
                current = current.getLeft();
            else
                current = current.getRight();
        }

        System.out.println (current.getRootElement());
    }
}

跟本來代碼比較類似,改一下字符串和樹的構造順序就能夠了
實現以下:

(4) 實驗四

(5) 實驗五

在查找數上實現findMin和findMax方法,最小值就從根一直到最左,最大值就是從根一直到最右。

public T findMin() {
        BTNode<T> node = null;
        if (root != null){
            node = node.getLeft();}
            return (T) node;
        }
    public T findMax() {
        BTNode<T> node = null;
        if (root != null){
            node = node.getRight();}
        return (T) node;
    }

(6) 實驗六

參考連接1
參考連接2
參考連接3

HashMap繼承自AbstractMap,實現了Map接口。
Map接口定義了全部Map子類必須實現的方法。Map接口中還定義了一個內部接口Entry(爲何要弄成內部接口?改天還要學習學習)。Entry將在後面有詳細的介紹。
AbstractMap也實現了Map接口,而且提供了兩個實現Entry的內部類:SimpleEntry和SimpleImmutableEntry。
定義了接口,接口中又有內部接口,而後有搞了個抽象類實現接口,抽象類裏面又搞了兩個內部類實現接口的內部接口

/**
     * 默認的初始容量,必須是2的冪。
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;
    /**
     * 最大容量(必須是2的冪且小於2的30次方,傳入容量過大將被這個值替換)
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;
    /**
     * 默認裝載因子,這個後面會作解釋
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    /**
     * 存儲數據的Entry數組,長度是2的冪。看到數組的內容了,接着看數組中存的內容就明白爲何博文開頭先複習數據結構了
     */
    transient Entry[] table;
    /**
     * map中保存的鍵值對的數量
     */
    transient int size;
    /**
     * 須要調整大小的極限值(容量*裝載因子)
     */
    int threshold;
    /**
     *裝載因子
     */
    final float loadFactor;
    /**
     * map結構被改變的次數
     */
    transient volatile int modCount;
    接着是HashMap的構造方法。

/**
     *使用默認的容量及裝載因子構造一個空的HashMap
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);//計算下次須要調整大小的極限值
        table = new Entry[DEFAULT_INITIAL_CAPACITY];//根據默認容量(16)初始化table
        init();
    }
/**
     * 根據給定的初始容量的裝載因子建立一個空的HashMap
     * 初始容量小於0或裝載因子小於等於0將報異常 
     */
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)//調整最大容量
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        int capacity = 1;
        //設置capacity爲大於initialCapacity且是2的冪的最小值
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }
/**
     *根據指定容量建立一個空的HashMap
     */
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);//調用上面的構造方法,容量爲指定的容量,裝載因子是默認值
    }
/**
     *經過傳入的map建立一個HashMap,容量爲默認容量(16)和(map.zise()/DEFAULT_LOAD_FACTORY)+1的較大者,裝載因子爲默認值
     */
    public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        putAllForCreate(m);
    }

TreeMap有Values、EntrySet、KeySet、PrivateEntryIterator、EntryIterator、ValueIterator、KeyIterator、DescendingKeyIterator、NavigableSubMap、AscendingSubMap、DescendingSubMap、SubMap、Entry共十三個內部類。
Values
// 從類的定義能夠看出,Values是一個集合類

class Values extends AbstractCollection<V> {
    // 提供集合類Values的迭代器
    public Iterator<V> iterator() {
        return new ValueIterator(getFirstEntry());
    }
    // 返回TreeMap中保存的節點數
    public int size() {
        return TreeMap.this.size();
    }
    // 判斷TreeMap中是否存在Value爲o的節點
    public boolean contains(Object o) {
        return TreeMap.this.containsValue(o);
    }
    // 刪除一個對象
    public boolean remove(Object o) {
        // 遍歷TreeMap
        for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e)) {
            // 尋找值相等的節點
            if (valEquals(e.getValue(), o)) {
                // 刪除找到的節點
                deleteEntry(e);
                return true;
            }
        }
        return false;
    }
    // 清空TreeMap
    public void clear() {
        TreeMap.this.clear();
    }
}

Values類其實是一個代理,多數方法都是調用TreeMap的方法。在Values的iterator()方法中返回了一個ValuesIterator對象,下面來看和迭代器相關的內部類。PrivateEntryIterator是TreeMap中和迭代器相關的類的基礎,如下是PrivateEntryIterator的內容。

abstract class PrivateEntryIterator<T> implements Iterator<T> {
    // 指向next的引用
Entry<K,V> next;
// 保留對上一次返回節點的引用
    Entry<K,V> lastReturned;
    int expectedModCount;
    // 構造方法,lastReturned置空,next指向傳入的節點
    PrivateEntryIterator(Entry<K,V> first) {
        expectedModCount = modCount;
        lastReturned = null;
        next = first;
    }
    // 判斷是否還有下一個節點
    public final boolean hasNext() {
        return next != null;
    }
    // 返回下一個節點
    final Entry<K,V> nextEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // next移動到它的繼承者
        next = successor(e);
        // 記錄被返回的節點
        lastReturned = e;
        // 返回原先記錄的next節點
        return e;
    }
    // 前一個節點
    final Entry<K,V> prevEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // 獲取指定節點的「前任」(按遍歷次序的前一個節點)
        next = predecessor(e);
        // 記錄被返回的節點
        lastReturned = e;
        return e;
    }
    // 移除最近一次被返回的節點
    public void remove() {
        if (lastReturned == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        // deleted entries are replaced by their successors
        if (lastReturned.left != null && lastReturned.right != null)
            /* 若是被刪除節點有兩個孩子,刪除節點e的時候e的引用會被修改成指向原節點的繼承者,因此這裏先保留next對lastReturned的引用,這樣在刪除節點後就能獲取到繼承者的引用,繼而繼續遍歷樹 */
            next = lastReturned;
        // 刪除節點
        deleteEntry(lastReturned);
        expectedModCount = modCount;
        lastReturned = null;
    }
}

PrivateEntryIterator類的prevEntry()方法用到了predecessor(Entry<K,V> t)方法,下面對這個方法進行介紹。

predecessor(Entry<K,V> t)方法返回傳入節點的「前一個」節點,至於前一個節點是哪一個節點,這和樹的遍歷次序相關。根據successor(Entry<K,V> t)和predecessor(Entry<K,V> t)方法能夠推出TreeMap中樹的遍歷次序是中根遍歷(左孩子-根-右孩子)。
static <K,V> Entry<K,V> predecessor(Entry<K,V> t) {
    if (t == null)
        return null;
else if (t.left != null) {
    // 得到左孩子
        Entry<K,V> p = t.left;
        // 對左孩子進行遍歷,獲取左孩子最右的子孫
        while (p.right != null)
            p = p.right;
        return p;
} else {
    // 獲取t的父節點
        Entry<K,V> p = t.parent;
        Entry<K,V> ch = t; 
// 沿着右孩子向上查找繼承者,直到根節點或找到節點ch是其父節點的右孩子的節點
        while (p != null && ch == p.left) {
            ch = p;
            p = p.parent;
        }
        return p;
    }
}

統計本身的PSP(Personal Software Process)時間

步驟 耗時 百分比
需求分析 60min 13.6%
代碼實現 300min 68.1%
測試 60min 13.6%
分析總結 20min 4.5%
相關文章
相關標籤/搜索