20162317袁逸灏 第四周实验报告:实验二 树

20162317袁逸灏 第四周实验报告:实验二 树

实验内容

  • 实现二叉树
  • 中序先序序列构造二叉树
  • 决策树
  • 表达式树
  • 二叉查找树
  • 红黑树分析

实验要求

  • 参考教材p375,完成链树LinkedBinaryTree的实现
  • 基于LinkedBinaryTree,实现基于(中序,先序)序列构造唯一一棵二㕚树的功能
  • 完成PP16.6
  • 完成PP16.8
  • 完成PP17.1
  • 对Java中的红黑树(TreeMap,HashMap)进行源码分析,并在实验报告中体现分析结果

实验过程

实验二 - 1 - 实现二叉树

  • 实现getRight()方法

P375中的代码有一个getLeft()方法,通过画葫芦画瓢(滑稽)将

result.root = root.getLeft();

改为

result.root = root.getRight();

即可。

  • 实现contains(T taget)

代码中提供了一个find(T target)的方法,通过find(T target)方法来协助即可。

  • 实现preorder()、postorder()方法

preorder的方法是用于提供前序遍历树用的,postorder方法是用于体统后序遍历树用的。这个方法重点在于BTNode中的preorder()和postorder()方法。实现了BTNode方法才能实现在LinkedBinaryTree的preorder()和postorder()方法。

public void inorder (ch16.ArrayIterator<T> iter)
    {
        //左子树
        if (left != null)
            left.inorder (iter);

        //根
        iter.add (element);

        //右子树
        if (right != null)
            right.inorder (iter);
    }

BTNode中有个 inorder()中序遍历树的方法,里面有三段函数,分别代表着查看左子树、根和右子树,这正是中序遍历的顺序。因此,只要将该三段函数位置交换即可:

BTNode.java

public void preorder(ch16.ArrayIterator<T> iter) {
     iter.add (element);

     if (left != null)
         left.preorder (iter);

     if (right != null)
         right.preorder (iter);
 }
public void postorder(ArrayIterator<T> iter) {


     if (left != null)
         left.postorder (iter);

     if (right != null)
         right.postorder (iter);

     iter.add (element);
 }

回到LinkedBinaryTree。同样,已经给出了inorder()方法的代码,剩下的只需要模仿即可:

LinkedBinaryTree.java

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

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

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

运行效果:

  • 实现toString方法

我认为那么多方法中,这个方法是最难的,根据老师要求,要有这种效果:

这种表示的方法的要点有:

  • 1、元素的左子结点存储在(2n+1)的位置,元素的右子结点存储在(2X(n+1))的位置上
  • 2、在最后一层之前,中间结点若只有一个子结点或为叶结点,要将位置空出来,最后一层的时候不需要这样的情况。

关于要点的解决办法:

  • 要点1:开始的思路是想弄一个数组,这样容易用下标来进行位置的安放。但随着深入的思考与探索,发现实际上不用这么复杂,实际上的效果就是这样:

由此可见,要用到层序遍历来实现这一效果。

  • 要点2:我的想法是首先通过树的性质来获得树的高度,同时可以知道每一层的元素数量是2^(n-1)那么多个。弄个双循环,外循环来负责层数,内循环负责元素个数,并在循环内设定条件语句:若不是最后一层,左/右结点不为空就填入,为空就填个空进去;当是最后一层的时候,就直接填入即可。

效果图:

实验二 - 2 - 中序先序序列构造二叉树

(P.S.该代码一定程度上可以上网查找——原码来源:http://blog.csdn.net/huangcan0532/article/details/46776749

要想实现构造二叉树,就要知道先序和后序如何构造的唯一的一棵二叉树。

首先看先序的第一个元素作为根,然后在中序中找到该元素的位置,将剩余的元素分位左子树和右子树。再从两棵子树的前序中找第一个元素作为根,然后分别在这两棵子树的中序遍历中找到对应元素,将其再次一分为二,重复这样做,最后通过先序和后序获得一棵树。

这里想要实现以上功能最关键的地方在于递归,递归可以重复调用自己的方法,就可以不断生成左子树和右子树。

效果:

实验二-3-决策树

这个实验没有太重点的地方,主要在于这个任务是个体力活(在于设定问题和答案以及之间的逻辑结构问题)。该试验实际上想表达的是树所能完成的任务。决策树就是其中之一,除了这次完成的实验,日后的决策树还可以作为一个预测模型,他代表的是对象属性与对象值之间的一种映射关系。可以用在开发上来评价项目或步骤的风险。

实验效果:

实验二-4-表达式树

该试验的难点就在于如何实现。我们可以使用后缀表达式来建立一棵树,我们先建立一个栈来存放结点,然后我们将输入的中缀表达式转化为后缀表达式,再遍历该后缀表达式表达式。如果遇到是操作数,那么就建立一个单结点树并将它推入栈中。如果符号是操作符,那么就从栈中弹出两棵树T1和T2(T1先弹出)并形成一棵新的树,该树的根就是操作符,它的左、右儿子分别是T2和T1。然后将指向这颗树的指针压入栈中。这棵树使用中序遍历的时候得到的是中缀表达式,后序遍历的时候得到的是后缀表达式,然后进行计算即可。

实验效果:

实验二 树-5-二叉查找树

该实验用到了第17章的二叉查找树

关于本人二叉查找树的可以浏览该博客:20162317 2017-2018-1 《程序设计与数据结构》第8周学习总结

该实验的要求是将findMin()方法和findMax()方法实现。根据二叉查找树的性质:

1、比根节点要小的数会放在当前根节点的左子结点,因此要实现findMin()只要获取该树的最左边的结点即是最小值

@Override
    public T findMin() {
        if(root==null){
            return null;
        }
        if(root!=null&&root.getLeft()==null){
            return root.getElement();
        }
        BTNode MinNode = root;
        while (MinNode.getLeft()!=null){
            MinNode = MinNode.getLeft();
        }
        return (T) MinNode.getElement();
    }
    

2、比根节点要大的数会放在当前根节点的右子结点,因此要实现findMax()只要获取该树的最右边的结点即是最大值

@Override
    public T findMax() {
        if(root==null){
            return null;
        }
        if(root!=null&&root.getRight()==null){
            return root.getElement();
        }
        BTNode MaxNode = root;
        while (MaxNode.getRight()!=null){
            MaxNode = MaxNode.getRight();
        }
        return (T) MaxNode.getElement();
    }

测试效果:

实验二 树-6-红黑树分析

TreeMap

先介绍TreeMap中的几个基本变量,中途会用得比较多。


1、

private final Comparator<? super K> comparator;

用于保持顺序的比较器,如果为空的话使用自然顺保持Key的顺序。

2、

private transient Entry<K,V> root = null;

根节点。

3、

private transient int size = 0;

树中的节点数量。

4、

private transient int modCount = 0;

用来记录树结构改变的次数。


remove(Object key)

public V remove(Object key) {
      
  Entry<K,V> p = getEntry(key);
      if (p == null)
          return null;
  V oldValue = p.value;
 deleteEntry(p);
     return oldValue;
 }

该方法先创建一个结点,通过getEntry(Object key)获取节点,若制定结点不在就返回null。然后获取结点的值(value),获取后将对应其删除,最后再返回结点的内容。

containsKey(Object key)

public boolean containsKey(Object key) {
     return getEntry(key) != null;
 }

通过获取包含该值的结点,查看是否为空。

HashMap

put(K key V value)

public V put(K key, V value) {
         if (key == null) 
            return putForNullKey(value);
         int hash = hash(key.hashCode());
         int i = indexFor(hash, table.length);
         for (Entry<K,V> e = table[i]; e != null; e = e.next) { 
             Object k;
              if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { 
                  V oldValue = e.value;
                 e.value = value;
                 e.recordAccess(this);
                 return oldValue;
              }
         }
    
         modCount++;
     
     addEntry(hash, key, value, i);
     return null;
}

若传入的键为空,将这个键值对添加到一个创建好的数组table的第一位tanle[0]。若键不为空,则计算该键的值,然后将其添加到对应的链表中,然后搜索指定hash值在对应table中的索引。循环遍历Entry数组,如果已存在该键,就用新的value取代旧的value,然后退出方法。

putAll(Map<? extends K, ? extends V> m)

 public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;

       
        if (table == EMPTY_TABLE) {
            
            inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold));
        }

        
        if (numKeysToBeAdded > threshold) {
           
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
         
            int newCapacity = table.length;
            
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;

            
            if (newCapacity > table.length)
                resize(newCapacity);
        }

首先获取传入数组table的键值对的数量,如果本地数组table为空,则新建一个数组,如果传入map的键值对数比“下一次扩容后的内部数组大小”还大,则对数组进行扩容。(因为当前数组即使扩容后也装不下它)

实验知识点

  • 二叉树
  • 结点
posted @ 2017-10-29 08:06  FunnyOne  阅读(312)  评论(0编辑  收藏  举报