20172329 2018-2019-2 《Java软件结构与数据结构》实验二报告

20172329 2018-2019-2 《Java软件结构与数据结构》实验二报告

课程:《Java软件结构与数据结构》php

班级: 1723html

姓名: 王文彬java

学号:20172329node

实验教师:王志强git

实验日期:2018年11月7日web

必修/选修: 必修算法

一.实验内容

1.1 第一个实验内容

  • 要求
    • (1)参考课本P212使用链表实现二叉树进行对于课本代码的完善以及补全。
    • (2)实现方法getRight方法,contains方法,toString方法,preorder方法,postorder方法。
    • (3)用JUnit或本身编写驱动类对本身实现的LinkedBinaryTree进行测试
    • (4)提交测试代码运行截图,要全屏,包含本身的学号信息,上传代码至码云,并提交码云连接。

1.2 第二个实验内容

  • 要求
    • (1)基于LinkedBinaryTree,实现基于(中序,先序)序列构造惟一一棵二㕚树的功能。
    • (2)好比给出中序HDIBEMJNAFCKGL和前序ABDHIEJMNCFGKL,构造出附图中的树。
    • (3)用JUnit或本身编写驱动类对本身实现的类进行测试
    • (4)提交测试代码运行截图,要全屏,包含本身的学号信息,上传代码至码云,并提交码云连接。

1.3 第三个实验内容

  • 要求
    • (1)本身设计并实现一颗决策树
    • (2)用JUnit或本身编写驱动类对本身实现的类进行测试
    • (3)提交测试代码运行截图,要全屏,包含本身的学号信息,上传代码至码云,并提交码云连接

1.4 第四个实验内容

  • 要求
    • (1)输入中缀表达式,使用树将中缀表达式转换为后缀表达式,并输出后缀表达式和计算结果
    • (2)用JUnit或本身编写驱动类对本身实现的类进行测试
    • (3)提交测试代码运行截图,要全屏,包含本身的学号信息,上传代码至码云,并提交码云连接

1.5 第五个实验内容

  • 要求
    • (1)完成PP11.3:对于二叉树查找树的链表实现,请实现removeMax方法,findMin方法和findMax方法以及后绪方法。
    • (2)用JUnit或本身编写驱动类对本身实现的类进行测试
    • (3)提交测试代码运行截图,要全屏,包含本身的学号信息,上传代码至码云,并提交码云连接

1.6 第六个实验内容

  • 要求
    • (1)参考http://www.cnblogs.com/rocedu/p/7483915.html对Java中的红黑树(TreeMap,HashMap)进行源码分析,并在实验报告中体现分析结果。

2、实验过程及结果

2.1 第一个实验过程

  • 步骤:
    • (1)第一个实验是有关对于代码补全的实验实现,其中包括实现方法getRight方法,contains方法,toString方法,preorder方法,postorder方法。咱们将一个一个进行实验而且进行代码分析。
  • 代码分析:

(1)getRight方法数组

public LinkedBinaryTree<T> getRight()
    {
        LinkedBinaryTree node = new LinkedBinaryTree();
        node.root=root.getRight();
       // return new LinkedBinaryTree(root.getRight());
       return node;
    }

分析:该方法经过先进行一个新树的初始化,使得咱们能够作一个临时树的角色,看到上述代码中注释的那一行,那是我第一次写的,是一个错误的代码,是由于他老是会获得根也就是root的右侧的树或结点,可是咱们须要获得的右子树或者有结点是变化的,假如按照以前写的,就会致使没法进行递归,程序就死在根上了。咱们经过初始化下面一行的代码进行根的更新,使咱们获得合适的右子树。安全

(2)contains方法数据结构

public boolean contains(T targetElement)
    {
        if (find(targetElement)==targetElement){
        return true;
    }
    else {
            return false;
        }

    }
    public T find(T targetElement) throws ElementNotFoundException
    {
        BinaryTreeNode<T> current = findNode(targetElement, root);

        if (current == null)
            throw new ElementNotFoundException("LinkedBinaryTree");

        return (current.getElement());
    }
    private BinaryTreeNode<T> findNode(T targetElement,
                                        BinaryTreeNode<T> next)
    {
        if (next == null)
            return null;

        if (next.getElement().equals(targetElement))
            return next;

        BinaryTreeNode<T> temp = findNode(targetElement, next.getLeft());

        if (temp == null)
            temp = findNode(targetElement, next.getRight());

        return temp;
    }

分析:该方法嵌套了另外的两个方法,写这个方法的逻辑就是,首先咱们须要找到这个结点,假如找到了这个结点就返回true,假如没有找到就返回false。如今咱们须要清楚查找这个结点的过程是如何进行的。首先,咱们须要清楚咱们须要从哪一个结点开始找,在这里就有人有疑问了,为何不老是设定成从根开始找,其实,假如像数据量小这样找还ok,可是假如在数据量庞大的时候,有这样一个设定能够很大的节约咱们查找元素的速度。

(3)toString方法

public String toString()
    {
        UnorderedListADT<BinaryTreeNode> nodes =
                new ArrayUnorderedList<BinaryTreeNode>();
        UnorderedListADT<Integer> levelList =
                new ArrayUnorderedList<Integer>();
        BinaryTreeNode current;
        String result = "";
        int printDepth = this.getHeight();
        int possibleNodes = (int)Math.pow(2, printDepth + 1);
        int countNodes = 0;

        nodes.addToRear(root);
        Integer currentLevel = 0;
        Integer previousLevel = -1;
        levelList.addToRear(currentLevel);

        while (countNodes < possibleNodes)
        {
            countNodes = countNodes + 1;
            current = nodes.removeFirst();
            currentLevel = levelList.removeFirst();
            if (currentLevel > previousLevel)
            {
                result = result + "\n\n";
                previousLevel = currentLevel;
                for (int j = 0; j < ((Math.pow(2, (printDepth - currentLevel))) - 1); j++)
                    result = result + " ";
            }
            else
            {
                for (int i = 0; i < ((Math.pow(2, (printDepth - currentLevel + 1)) - 1)) ; i++)
                {
                    result = result + " ";
                }
            }
            if (current != null)
            {
                result = result + (current.getElement()).toString();
                nodes.addToRear(current.getLeft());
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(current.getRight());
                levelList.addToRear(currentLevel + 1);
            }
            else {
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                nodes.addToRear(null);
                levelList.addToRear(currentLevel + 1);
                result = result + " ";
            }

        }

        return result;
    }

分析:这个代码其实就是教材中表达式树输出成一棵树的方法,具体详见个人第六周博客,在其中的课本问题三中有详细的分析。

(4)preorder方法

public Iterator<T> iteratorPreOrder()
    {
        ArrayUnorderedList<T> tempList = new ArrayUnorderedList<T>();
       preOrder(root, tempList);

        return new TreeIterator(tempList.iterator());
    }
    
protected void preOrder(BinaryTreeNode<T> node,
                            ArrayUnorderedList<T> tempList)
    {
        if(node!=null)
            {
               System.out.print(node.getElement()+" ");
                tempList.addToRear(node.getElement());
                preOrder(node.getLeft(),tempList);
                preOrder(node.getRight(),tempList);
            }

    }

分析:这一部分实则运用的是迭代器的方法,前序遍历实现讲分析与后序大体相同,在这里只分析前序,首先咱们要清楚前序遍历的顺序,在这里假若有不清楚的同窗能够参考个人第六周博客,教材内容总结部分。当咱们清楚了前序遍历是如何进行的之后,咱们就能够大体了解这个递归的运行原理了。其中迭代器的做用为的是咱们能够将其进行一个一个的输出。

  • 实验实现结果展现:

2.2 第二个实验过程

  • 步骤:

    • 第二个实验是须要咱们去利用中序和前序去构造出一颗二叉树,咱们须要清楚的有如下步骤:

    • (1)首先咱们在课上练习过如何去利用中序和前序去构造出一颗树,在这里我再进行说明;

    • (2)有这样一句话方便理解前(或后)序定根,中序定左右,如何理解这句话呢,举个例子,就拿咱们题目中的进行分析。前序是:ABDHIEJMNCFGKL,中序是:HDIBEMJNAFCKGL。

      • 一、首先咱们看前序的第一个元素,是A则其确定是根,因此在中序中找到A,如今在中序遍历中A左边的就是A的左子树,A右边的就是A的右子树;

      • 二、一样咱们继续找前序的第二个元素,是B,因此再看中序中B的位置,一样和理解A的左右同理,咱们能够找到B结点的左子树和右子树。

      • 三、紧接着重复上述直到找到中序的第一个元素为至,咱们就中止,如今咱们构造好的是整个树的左子树(左树的左树);

      • 四、如今开始看A的左子树的右子树的完善工做,由于D是左边最后一个根,因此从中序中能够得知,H是D的左孩 子,I是D的右孩子。

      • 五、咱们继续向上找,发现根是B,B的左边咱们已经构造完成了,因此咱们如今须要构造出B的右边,EMJN是咱们如今须要构造出的,咱们看中序,咱们看到E是处于最左边的,说明它是一个左孩子或者是一个根,再紧接着看,前序,发现E是第一个,因此说明E是一个根,因此咱们肯定了B的右根为E;

      • 六、由于在看到中序E后面的是M,因此,和第5步的理解相同,发现一样M是一个根,如今再看前序,发现J和N在M的两侧,即分别是M的左子树和右子树。

      • 七、肯定了A的左子树了之后咱们继续看A的右子树,中序是:FCKGL,由于在前序中C在F以前,因此否认F是根,F是C的左子树,因此如今能够知道A的右子树的根是C,左子树是F,再根据一样的原理,能够得知,G为C的右根,KL分别是G的左孩子和右孩子。

      • 八、经过这样一个过程就构建出一棵树了。

    • (3)根据这样一个思想咱们开始进行对于代码的编写。

  • 代码分析

public int find(T[] a,T element,int begin,int end){
        for(int i = begin;i<=end; i++){
            if(a[i]==element){
                return i;
            }
        }
        return -1;
    }

    public BinaryTreeNode<T> construct(T[] pre,int prestart,int preend,T[] inO,int inOSart,int inOend){
        if(prestart>preend||inOSart>inOend){
            return null;
        }
        BinaryTreeNode Root;
        T rootData = pre[prestart];
         Root = new BinaryTreeNode(rootData);
        //找到根节点所在位置
        int rootIndex = find(inO,rootData,inOSart,inOend);
        //构建左子树
        Root.left = construct(pre,prestart+1,prestart+rootIndex-inOSart,inO,inOSart,rootIndex-1);
       // 构建右子树

        Root.right = construct(pre,prestart+rootIndex-inOSart+1,preend,inO,rootIndex+1,inOend);
         return Root;

        }

        public void ConTree(T[] pre,T[] inO){
        BinaryTreeNode<T> node = construct(pre,0,pre.length-1,inO,0,inO.length-1);
        root=node;
        }

分析:咱们在这个主方法定义了六个变量,在construct方法的做用分别为:传入一个先序遍历结果(数组),肯定先序遍历中开始的位置,肯定先序遍历中结束的位置,传入一个中序遍历结果(数组),肯定中序遍历中开始的位置,肯定中序遍历中结束的位置。如今咱们开始看方法体的内容,咱们首先肯定一个东西,就是根确定是传入先序遍历的第一个元素,因此先肯定根的位置,找到根之后,咱们开始安排找到它的子树们,咱们经过一个find方法进行对于咱们须要元素在中序遍历中的查找,由于咱们能够经过一个规律,就是对于传入的元素查找后获得的值(1或者-1)进行对于左孩子仍是右孩子的查找,从而能够完成一棵树的构造。

  • 实验实现结果展现

2.3 第三个实验过程

  • 步骤

    • 第三个实验是让咱们本身写一个决策树,对于这个实验我基于课本代码进行了仿写进行实验。
  • 代码分析:
public DecisionTree(String filename) throws FileNotFoundException
    {
        File inputFile = new File(filename);
        Scanner scan = new Scanner(inputFile);
        int numberNodes = scan.nextInt();
        scan.nextLine();
        int root = 0, left, right;
        
        List<LinkedBinaryTree<String>> nodes = new ArrayList<LinkedBinaryTree<String>>();
        for (int i = 0; i < numberNodes; i++)
            nodes.add(i,new LinkedBinaryTree<String>(scan.nextLine()));
        
        while (scan.hasNext())
        {
            root = scan.nextInt();
            left = scan.nextInt();
            right = scan.nextInt();
            scan.nextLine();
            
            nodes.set(root, new LinkedBinaryTree<String>((nodes.get(root)).getRootElement(), 
                                                     nodes.get(left), nodes.get(right)));
        }
        tree = nodes.get(root);
    }
    public void evaluate()
    {
        LinkedBinaryTree<String> current = tree;
        Scanner scan = new Scanner(System.in);

        while (current.size() > 1)
        {
            System.out.println (current.getRootElement());
            if (scan.nextLine().equalsIgnoreCase("N"))
                current = current.getLeft();
            else
                current = current.getRight();
        }

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

分析:这一部分的代码由于都是书中的代码,本身就照搬了,(本身太懒了)在这代码中首先咱们利用一个txt文档对于书中的文档进行了层序的保存,经过读取文件的方式咱们进行决策树的构造,其中第一个方法(DecisionTree)咱们将文件读取后将其内容一个一个放入一个链表,而后经过层序进行把元素放入树中;第二个方法(evaluate)用于判断当咱们输入的是N或者其余时,进行对于元素的输出。

  • 实验实现结果展现

2.4 第四个实验过程

  • 步骤

    • 第四个实验要求咱们输入一个中缀表达式,经过一个二叉树转换成后缀表达式,再将其输出后进行计算,首先咱们须要清楚这个实验应该如何去进行。

    • (1)第四个实验一开始我画了不少中树图进行表示,最后本身认为假如实现的话有两种方式:

      • 一、由于输入的中缀表达式可能涉及咱们须要面临括号的问题,或者也就是咱们须要去如何去解决优先级的问题,在这时候个人第一种想法是:先将一个中缀表达式按照优先级进行排序,好比,1+(1+2✖3,我首先将1+2放到最前其次是 *3,最后是+1,最后结果就是1+2✖3+1 ,而后将数字与操做符存入两个栈或者链表,分别进行弹出,自左向右,造成一棵树;可是这个方法由于假如涉及两个括号就变得较为复杂,由于这个时候须要构建右子树,因此就将这个方法淘汰了。

      • 二、第二站方法也就是最终进行实现的方法,首先咱们输入一个中缀表达式,将表达式的每个元素进行拿出,按照“数字”,“括号”,“加减”,“乘除”分红四种状况,用两个栈或者链表进行保存“加减”和“构成树的结点”。这种方法会使得方法实现变得简单易行。

  • 代码分析
//由于代码的庞大,因此就关键代码进行分析,循环和输出的过程简单,在
//这里不作分析,在这里分析如何对于每个元素进行分析
//这个代码参考了以前学长的代码,而且请教了学长本人,经过学长的讲授受益不浅。
if (a[i].equals("(")){
    String temp1 = "";
    while (true) {
    if (!a[i+1].equals(")")){
     temp1 += a[i+1] + " ";
            }
            else {
            break;
        }
            i++;
            }
        chapter10.jsjf.LinkedBinaryTree temp = new chapter10.jsjf.LinkedBinaryTree();
        temp.root = maketree(temp1);
         num.add(temp);
         i++;
            }
         if (a[i].equals("+")||a[i].equals("-")){
     fuhao.add(String.valueOf(a[i]));
             }
      else if (a[i].equals("*")||a[i].equals("/")){
    LinkedBinaryTree left=num.remove(num.size()-1);
     String temp2=String.valueOf(a[i]);
     if (!a[i+1].equals("(")){
     LinkedBinaryTree right = new LinkedBinaryTree(String.valueOf(a[i+1]));
     LinkedBinaryTree node = new LinkedBinaryTree(temp2, left, right);
    num.add(node);
        }
        else {
        String temp3 = "";
        while (true) {
        if (!a[i+1].equals(")")){
        temp3+=String.valueOf(a[i+1]);
        }
        else {
         break;
        }
        i++;
        }
        LinkedBinaryTree temp4 = new LinkedBinaryTree();
        temp4.root = maketree(temp3);
        LinkedBinaryTree node1 = new LinkedBinaryTree(temp2, left, temp4);
        num.add(node1);
       }
       }else {
        num.add(new LinkedBinaryTree(a[i]));}

分析:从上述的代码就能够看到须要的逻辑之复杂,当时在学习过程就实验四真的是绞尽脑汁。首先,咱们须要作的事情是先了解咱们这个树是如何“长”成一棵树的,由于优先级最高的是括号,因此括号的这一部分就是咱们的叶子,咱们这个树是倒着长,从叶子长到根,所以咱们就须要针对括号进行优先处理,因此咱们先写括号。一、当咱们遇到‘(’的时候,咱们要作的一件事就是须要将直至‘)’内的元素都进行一个保存,由于说明这一部分咱们须要优先处理它,当咱们将这一部分进行保存了之后咱们利用一个递归,开始处理这一部分;二、处理括号内部分的过程和处理括号外是同样的,只是括号须要优先处理,如今咱们开始分析当咱们进行括号或者一个相似与‘1+2✖6’之类的式子进行处理;三、当咱们遇到数字的时候,咱们将其保存进数字的链表(树类型,也就是保存进一个以这个数字为根的小树)中,而后循环;四、当咱们遇到‘+或者-’的时候,将其保存进一个存符号的链表,而后循环;五、当咱们遇到‘✖或者➗的时候,咱们首先须要将咱们以前存入数字的链表的元素进行拿出而且进行将其作一个新树,由于在画过这种树图的同窗来说,很清楚一个问题,就是加法确定会跟靠近树根,因此咱们要将咱们以前放入数字链表的元素拿出来,为作一个左树作好准备,而后判断一下‘✖️或者➗’ 后有没有括号,假若有括号咱们仍旧须要优先处理,按照1,2操做,,构造好一个右树,而后当咱们处理好这一系列问题之后,就能够以这个+或者-为根,将刚刚分析的作好准备的左树和刚刚构造好的右树进行放在这个根的下面,分别作它的左子树和右子树,而后重复这个过程,直至没有元素可找。这就是整个实验四在遇到各个符号的状况。

  • 实验实现结果展现

2.5 第五个实验过程

  • 步骤:

    • (1)第五个实验同时咱们和第一个实验四类似,都是进行代码补全,进行分析。

    • (2)实现removeMax方法,findMin方法和findMax方法以及后绪方法。

  • 代码分析:

(1)removeMax方法

public T removeMax() throws EmptyCollectionException
    {
        T result = null;

        if (isEmpty())
            throw new EmptyCollectionException("LinkedBinarySearchTree");
        else
        {
            if (root.right == null)
            {
                result = root.element;
                root = root.left;
            }
            else
            {
                BinaryTreeNode<T> parent = root;
                BinaryTreeNode<T> current = root.right;
                while (current.right != null)
                {
                    parent = current;
                    current = current.right;
                }
                result =  current.element;
                parent.right = current.left;
            }

            modCount--;
        }

        return result;
    }

由于咱们知道对于一个树而言,最左边是最小值,最右边是最大值,因此咱们假如要删除最小值的话,就须要先找到这个最小值,而后让它为空,而且返回它,删除最大值也是一样的道理。而找到最小值最大值一样也只是这一部分代码的一部分,同理。

  • 实验实现结果展现

2.6 第六个实验过程

  • 步骤:

    • 最后一个实验是让咱们对Java中的红黑树(TreeMap,HashMap)进行源码分析,首先既然是红黑树的两个方法,因此在开始的时候咱们要去了解红黑树是什么,具体能够详见个人第七周博客

    • 首先既然两个都是map结尾的,说明map也是一个类,咱们先来看看map是什么?

    • map:Map接口中键和值一一映射. 能够经过键来获取值。

      • (1)给定一个键和一个值,你能够将该值存储在一个Map对象. 以后,你能够经过键来访问对应的值。
      • (2)当访问的值不存在的时候,方法就会抛出一个NoSuchElementException异常.
      • (3)当对象的类型和Map里元素类型不兼容的时候,就会抛出一个 ClassCastException异常。
      • (4)当在不容许使用Null对象的Map中使用Null对象,会抛出一个NullPointerException 异常。
      • (5)当尝试修改一个只读的Map时,会抛出一个UnsupportedOperationException异常。
      • 用代码举个例子:
import java.util.*;

public class CollectionsDemo {

   public static void main(String[] args) {
      Map m1 = new HashMap(); 
      m1.put("Zara", "8");
      m1.put("Mahnaz", "31");
      m1.put("Ayan", "12");
      m1.put("Daisy", "14");
      System.out.println();
      System.out.println(" Map Elements");
      System.out.print("\t" + m1);
   }
}
结果:
Map Elements
        {Mahnaz=31, Ayan=12, Daisy=14, Zara=8}
  • 在以上的程序中一样的也展现出了咱们一下子要分析的方法之一hashmap方法,以上的方法是用了其put方法,也就是让两个参数之间创建了一种映射。所以在这里咱们详细的列一下map这个类所拥有的方法。
序号 方法 描述
1 void clear( ) 今后映射中移除全部映射关系(可选操做)。
2 boolean containsKey(Object k) 若是此映射包含指定键的映射关系,则返回 true。
3 boolean containsValue(Object v) 若是此映射将一个或多个键映射到指定值,则返回 true。
4 Set entrySet( ) 返回此映射中包含的映射关系的 Set 视图。
5 boolean equals(Object obj) 比较指定的对象与此映射是否相等。
6 Object get(Object k) 返回指定键所映射的值;若是此映射不包含该键的映射关系,则返回 null。
7 int hashCode( ) 返回此映射的哈希码值。
8 boolean isEmpty( ) 若是此映射未包含键-值映射关系,则返回 true。
9 Set keySet( ) 返回此映射中包含的键的 Set 视图。
10 Object put(Object k, Object v) 将指定的值与此映射中的指定键关联(可选操做)。
11 void putAll(Map m) 从指定映射中将全部映射关系复制到此映射中(可选操做)。
12 Object remove(Object k) 若是存在一个键的映射关系,则将其今后映射中移除(可选操做)。
13 int size( ) 返回此映射中的键-值映射关系数。
14 Collection values( ) 返回此映射中包含的值的 Collection 视图。
  • 在了解了这个map类之后咱们开始步入咱们的正题。

  • treemap方法:
    • 咱们首先先了解一下这个类:
      • 一、TreeMap 是一个有序的key-value集合,它是经过红黑树实现的。
      • 二、TreeMap 继承于AbstractMap,因此它是一个Map,即一个key-value集合。
      • 三、TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。好比返回有序的key集合。
      • 四、TreeMap 实现了Cloneable接口,意味着它能被克隆。
      • 五、TreeMap 实现了java.io.Serializable接口,意味着它支持序列化。
      • 六、TreeMap基于红黑树(Red-Black tree)实现。该映射根据其键的天然顺序进行排序,或者根据建立映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
      • 七、TreeMap的基本操做 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。
      • 八、TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。
    • 源码分析
      我在这里经过网上其余博主的参考和本身的理解对此方法进行了分析,由于代码的庞大,因此在这里放置代码连接。重要的一些方法在这里我进行分析。

(1)了解构造函数

// 带比较器的构造函数
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }

    // 带Map的构造函数,Map会成为TreeMap的子集
    public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(m);
    }

    // 带SortedMap的构造函数,SortedMap会成为TreeMap的子集
    public TreeMap(SortedMap<K, ? extends V> m) {
        comparator = m.comparator();
        try {
            buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
        } catch (java.io.IOException cannotHappen) {
        } catch (ClassNotFoundException cannotHappen) {
        }
    }

(2)了解咱们在其余数据结构中常见的几个方法

// 返回TreeMap中是否保护“键(key)”
    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    // 返回TreeMap中是否保护"值(value)"
    public boolean containsValue(Object value) {
        // getFirstEntry() 是返回红黑树的第一个节点
        // successor(e) 是获取节点e的后继节点
        for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
            if (valEquals(value, e.value))
                return true;
        return false;
    }

    // 获取“键(key)”对应的“值(value)”
    public V get(Object key) {
        // 获取“键”为key的节点(p)
        Entry<K,V> p = getEntry(key);
        // 若节点(p)为null,返回null;不然,返回节点对应的值
        return (p==null ? null : p.value);
    }

(3)其余详见码云

  • hashmap方法
    • 一开始看到这个方法有一丝疑问,为何在红黑树里面还会牵扯到哈希表,也就是散列表,而后就心存一位是否是应该是treeset方法,因此一下子仍是想继续分析treeset方法,可是首先我须要了解,为何散列表会和红黑树挂勾呢?
    • 咱们知道HashMap中的值都是key,value对吧,其实这里的存储与上面的很像,key会被映射成数据所在的地址,而value就在以这个地址为头的链表中,这种数据结构在获取的时候就很快。但这里存在的问题就是若是hash桶较小,数据量较大,就会致使链表很是的长。好比说上面的长为11的空间我要放1000个数,不管Hash函数如何精妙,后面跟的链表都会很是的长,这样Hash表的优点就不复存在了,反而倾向于线性检索。

    • 源码分析

//实际存储的key-value键值对的个数
transient int size;
//阈值,当table == {}时,该值为初始容量(初始容量默认为16);当table被填充了,也就是为table分配内存空间后,threshold通常为 capacity*loadFactory。HashMap在进行扩容时须要参考threshold,后面会详细谈到
int threshold;
//负载因子,表明了table的填充度有多少,默认是0.75
final float loadFactor;
//用于快速失败,因为HashMap非线程安全,在对HashMap进行迭代时,若是期间其余线程的参与致使HashMap的结构发生变化了(好比put,remove等操做),须要抛出异常ConcurrentModificationException
transient int modCount;
  • 构造函数:
public HashMap(int initialCapacity, float loadFactor) {
     //此处对传入的初始容量进行校验,最大不能超过MAXIMUM_CAPACITY = 1<<30(230)
        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);

        this.loadFactor = loadFactor;
        threshold = initialCapacity;
     
        init();//init方法在HashMap中没有实际实现,不过在其子类如 linkedHashMap中就会有对应实现
    }
  • get方法:
public V get(Object key) {
     //若是key为null,则直接去table[0]处去检索便可。
        if (key == null)
            return getForNullKey();
        Entry<K,V> entry = getEntry(key);
        return null == entry ? null : entry.getValue();
 }
  • treeset方法:

  • 构造函数:

序号 方法 描述
1 TreeSet () 此构造函数构造空树集,将在根据其元素的天然顺序按升序排序。
2 TreeSet (Collection c) 此构造函数生成树的集合,它包含的元素的集合 c。
3 TreeSet (Comparable comp) 此构造函数构造一个空树集,将根据给定的比较器进行排序。
4 TreeSet (SortedSet ss) 此构造函数生成包含给定 SortedSet 的元素 TreeSet
  • 方法:
序号 方法 描述
1 add(E e) 将指定的元素添加到这套,若是它已不存在。
2 addAll(Collection<? extends E> c) 在加入这一组指定的集合中添加的全部元素。
3 ceiling(E e) 返回最小的元素在这一组大于或等于给定的元素
4 clear() 从这一组中移除全部元素。
5 clone() 返回此TreeSet实例浅表副本。
6 comparator() 返回用于排序在这集,或空元素
7 contains(Object o) 若是此集合包含指定的元素,则返回true 。
8 descendingIterator() 返回迭代器中这套降序排序的元素。
9 descendingSet() 返回逆序视图中包含的元素这一套。
10 first() 返回第一个 (最低) 元素当前在这一套。
11 floor(E e) 返回的最大元素在这一组小于或等于null若是没有这样的元素。
12 headSet(E toElement) 返回其元素是严格小于toElement这套的部分视图.
13 headSet(E toElement, boolean inclusive) 返回一个视图的这部分设置的元素都小于
14 higher(E e) 返回最小的元素在这套严格大于给定的元素
15 isEmpty() 若是此集不包含任何元素,则返回true 。
16 iterator() 返回迭代器中这套以升序排序的元素。
17 last() 在这套目前返回的最后一个 (最高) 的元素。
18 lower(E e) 在这一套严格的小于给定的元素,则null返回的最大元素
19 pollFirst() 检索和删除第一个 (最低) 元素,或若是此集合为空
20 pollLast() 检索和删除的最后一个 (最高) 的元素
21 remove(Object o) 从这一组中移除指定的元素,若是它存在。
22 size() 在这套 (其基数) 中返回的元素的数目。
23 subSet(E fromElement, boolean fromInclusive, E toElement) 返回此集的部分视图的元素范围从fromElement到toElement.

3、 实验过程当中遇到的问题和解决过程

  • 问题1:在以前写第二个实验的时候发生了栈溢出的状况,以下

java.lang.StackOverflowError异常(异常的图丢了)

  • 问题1解决方案:

    • (1)我发现当咱们空写一个初始化的时候,用它递归的时候就会发生栈溢出。

    • (2)方法一:用栈把递归转换成非递归
      一般,一个函数在调用另外一个函数以前,要做以下的事情:

      • a)将实在参数,返回地址等信息传递给被调用函数保存;

      • b)为被调用函数的局部变量分配存储区;

      • c)将控制转移到被调函数的入口。从被调用函数返回调用函数以前,也要作三件事情:
        • a)保存被调函数的计算结果;
        • b)释放被调函数的数据区;
        • c)依照被调函数保存的返回地址将控制转移到调用函数.全部的这些,不管是变量仍是地址,本质上来讲都 是"数据",都是保存在系统所分配的栈中的. 那么本身就能够写一个栈来存储必要的数据,以减小系统负。
    • (3)方法二:使用static对象替代nonstatic局部对象
      在递归函数设计中,可使用static对象替代nonstatic局部对象(即栈对象),这不只能够减小每次递归调用和返回时产生和释放nonstatic对象的开销,并且static对象还能够保存递归调用的中间状态,而且可为各个调用层所访问。

  • 问题2:其他的问题在我搞懂实验的同时也都解决了,不少都写在上述的步骤过程和代码分析中进行了分析。
  • 问题2:详见实验过程

4、感想

在此次实验中,本身写了不少,同时也毙掉了不少版代码,算法真是个神奇的东西,我本身也参考了不少文章,不少博客才能完成此次实验,发现本身还差不少,本身还须要很努力才行。

5、参考文献

【数据结构】中缀表达式构造二叉树转换成后缀表达式
表达式树—中缀表达式转换成后缀表达式(一)
java实现二叉树已知先序遍历和中序遍历求后序遍历
根据中序和前序序列生成二叉树java递归实现
已知二叉树的中序和前序序列(或后序)求解树
Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)
蓝墨云班课
Java程序设计
Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例
Java Map 接口
HashMap实现原理及源码分析

相关文章
相关标签/搜索