zoukankan      html  css  js  c++  java
  • 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的键值对数比“下一次扩容后的内部数组大小”还大,则对数组进行扩容。(因为当前数组即使扩容后也装不下它)

    实验知识点

    • 二叉树
    • 结点
  • 相关阅读:
    Kill Processes in Linux
    How to Setup Chroot SFTP in Linux (Allow Only SFTP, not SSH)
    156 Useful Run Commands
    6
    pandas groupby合并列字符串
    一个ROS配置的脚本
    Mybatis 学习记录
    Android搭建code server
    CF 1616D. Keep the Average High
    第七章:(1)Redis 的发布订阅
  • 原文地址:https://www.cnblogs.com/VersionP1/p/7749281.html
Copyright © 2011-2022 走看看