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%
相关文章
相关标签/搜索