实验二 树-1-实现二叉树
参考教材p375,完成链树LinkedBinaryTree的实现(getRight,contains,toString,preorder,postorder)用JUnit或自己编写驱动类对自己实现的LinkedBinaryTree进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息,课下把代码推送到代码托管平台.
- getRight方法与LinkedBinaryTree中的getLeft相似
public LinkedBinaryTreegetRight() { if (root==null) throw new EmptyCollectionException("Get right operation"+"failed.The tree is empty."); LinkedBinaryTree result = new LinkedBinaryTree<>(); result.root=root.getRight(); return result; }
- contains方法调用find方法,返回值则是boolean类型
public boolean contains(T target) { BTNodenode = null; if (root!=null) node = root.find(target); if (node==null) return false; else return true; }
- preorder和postorder方法则和inorder方法不尽相似,但都是调用BTnode类的相应方法,并实现递归
public Iteratorpreorder() { ArrayIterator iter = new ArrayIterator<>(); if (root!=null) root.preorder(iter); return iter; } public Iterator inorder() { ArrayIterator iter = new ArrayIterator (); if (root != null) root.inorder (iter); return iter; } //BTnode public void inorder(ArrayIterator iter){ if (left!=null) left.inorder(iter); iter.add(element); if (right!=null) right.inorder(iter); } public void preorder(ArrayIterator iter){ iter.add(element); if (left!=null) left.preorder(iter); if (right!=null) right.preorder(iter); }
实验二 树-2-中序先序序列构造二叉树
基于LinkedBinaryTree,实现基于(中序,先序)序列构造唯一一棵二㕚树的功能,比如教材P372,给出HDIBEMJNAFCKGL和ABDHIEJMNCFGKL,构造出附图中的树,用JUnit或自己编写驱动类对自己实现的功能进行测试,提交测试代码运行截图,要全屏,包含自己的学号信息
课下把代码推送到代码托管平台.
思路:利用递归
总体:从先序遍历中找到二叉树的根节点,然后在中序找到该根节点的位置,并将中序序列分成左右两部分,左为左子树,右为右子树。在左子树中找到左子树的根节点,并在根据中序的位置划分左右子树(递归),在右子树中找到根节点,同理递归,最后返回构建好的树。
整体伪代码构思:
- 1.方法CreatTree(T [] pre,T [] inorder)传入两个数组,前序和中序
- 2.创建根节点传入参数为pre[0],利用Findposition方法找到根节点数据在中序中的位置。
- 3.分别创建数组来保存根据根节点所划分出的左右子树(左两个:先序和后序;右两个:先序和后序)
- 4.利用循环给创建的四个数组传入数据。
- 5.利用递归构建左子树,右子树。
public LinkedBinaryTreeCreatTree(T [] pre,T [] inorder){ if (pre.length==0||inorder.length==0||pre.length!=inorder.length){ return null; } LinkedBinaryTree mTree = new LinkedBinaryTree<>(pre[0]); int num = Findpostion(pre[0],inorder); T[] preLeft = (T[])(new Object[num]); T[] inorderLeft = (T[])(new Object[num]); T[] preRight = (T[])(new Object[pre.length-num-1]); T[] inorderRight = (T[])(new Object[inorder.length-num-1]); for(int j =0;j num){ preRight[j-num-1]=pre[j]; inorderRight[j-num-1]=inorder[j]; } } mTree.left= CreatTree(preLeft,inorderLeft); mTree.right = CreatTree(preRight,inorderRight); return mTree; }
实验三 树-3-决策树
完成PP16.6,提交测试代码运行截图,要全屏,包含自己的学号信息,课下把代码推送到代码托管平台.
public class QuesTwenty { private LinkedBinaryTreetree; public QuesTwenty(){ String q1 = "Is it an animal?"; String q2 = "Is it a mammal?"; String q3 = "Is it an electrical appliance?"; String q4 = "Does it have fat pads at the bottom of its toes?"; String q5 = "Can it swim?"; String q6 = "Can it blow?"; String q7 = "Does it have a backrest?"; LinkedBinaryTree n2,n3,n4,n5,n6,n7; n4 = new LinkedBinaryTree<>(q4); n5 = new LinkedBinaryTree<>(q5); n2 = new LinkedBinaryTree<>(q2,n4,n5); n6 = new LinkedBinaryTree<>(q6); n7 = new LinkedBinaryTree<>(q7); n3 = new LinkedBinaryTree<>(q3,n6,n7); tree = new LinkedBinaryTree<>(q1,n2,n3); } public void diagnose(){ Scanner scan = new Scanner(System.in); LinkedBinaryTree current = tree; System.out.println("OK? the items have Dog, cat, chicken, duck, fan, TV chair, bookcase"); 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()); System.out.println("You have run out all the questions , do you know the answer"); }}
实验二 树-4-表达式树
完成PP16.8,提交测试代码运行截图,要全屏,包含自己的学号信息,课下把代码推送到代码托管平台
- 构建表达式树,利用后序遍历得到表达式树所代表计算式的后缀表达,利用MyDC类进行计算。
public int calculate(){ StringBuilder mBuilder = new StringBuilder(); MyDC myDC = new MyDC(); ArrayIteratormIterator = (ArrayIterator ) this.postorder(); while (mIterator.hasNext()){ mBuilder.append(mIterator.next()); mBuilder.append(" "); } return myDC.evaluate(String.valueOf(mBuilder)) ; } }
实验二 树-5-二叉查找树
完成PP17.1,提交测试代码运行截图,要全屏,包含自己的学号信息,课下把代码推送到代码托管平台.
- 因为LinkedBinarySearchTree类中所有的add()方法,树构建之后就不难看出如何得到findMax(),findMin()方法.
public T findMax() { MyListmyList = new MyList<>(); if (root!=null) ((BSTNode)root).findMax(myList); return myList.getFirst(); } @Override public T findMin() { MyList myList = new MyList<>(); if (root!=null) ((BSTNode)root).findMin(myList); return myList.getFirst(); } public void findMin(MyList list){ if (((BSTNode)left)!=null){ ((BSTNode)left).findMin(list); } list.add(element); } public void findMax(MyList list){ if (((BSTNode)right)!=null){ ((BSTNode)right).findMax(list); } list.add(element); }
实验二 树-6-红黑树分析
红黑二叉树是一种特殊的排序二叉树。
排序二叉树要么是一棵空二叉树,要么是具有下列性质的二叉树:
- 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 它的左、右子树也分别为排序二叉树。
红黑树是一个更高效的检索二叉树。
- 每个节点要么是红色,要么是黑色。
- 根节点永远是黑色的。
- 所有的叶节点都是空节点(即 null),并且是黑色的.
- 每个红色节点的两个子节点都是黑色。(从每个叶子到根的路径上不会有两个连续的红色节点)
- 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。
Java实现的红黑树将使用Null来代替空节点,因此遍历红黑树时将看不到黑色的叶节点,反而看到每个叶子节点都是红色的。
- TreeMap添加节点
public V put(K key, V value) { Entryt = root; if (t == null) { // 将新的 key-value 创建一个 Entry,并将该 Entry 作为 root root = new Entry (key, value, null); // Map 集合的 size 为 1,包含一个 Entry size = 1; modCount++; return null; } int cmp; Entry parent; Comparator cpr = comparator; if (cpr != null) { do { // 使用 parent 上次循环后的 t 所引用的 Entry parent = t; // 拿新插入 key 和 t 的 key 进行比较 cmp = cpr.compare(key, t.key); // 如果新插入的 key 小于 t 的 key,t 等于 t 的左边节点 if (cmp < 0) t = t.left; // 如果新插入的 key 大于 t 的 key,t 等于 t 的右边节点 else if (cmp > 0) t = t.right; // 如果两个 key 相等,新的 value 覆盖原有的 value, // 并返回原有的 value else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); Comparable k = (Comparable ) key; do { // 使用 parent 上次循环后的 t 所引用的 Entry parent = t; // 拿新插入 key 和 t 的 key 进行比较 cmp = k.compareTo(t.key); // 如果新插入的 key 小于 t 的 key,t 等于 t 的左边节点 if (cmp < 0) t = t.left; // 如果新插入的 key 大于 t 的 key,t 等于 t 的右边节点 else if (cmp > 0) t = t.right; // 如果两个 key 相等,新的 value 覆盖原有的 value, // 并返回原有的 value else return t.setValue(value); } while (t != null); } // 将新插入的节点作为 parent 节点的子节点 Entry e = new Entry (key, value, parent); // 如果新插入 key 小于 parent 的 key,则 e 作为 parent 的左子节点 if (cmp < 0) parent.left = e; //与上一步相反 else parent.right = e; // 修复红黑树 fixAfterInsertion(e); size++; modCount++; return null; }
每次添加新节点,程序从输的根节点开始比较。循环:定义一个引用t,t指向根节点,如果新增节点大于当前t节点,并且当前t节点的右节点存在,则t指向该右节点;如果新增节点小于当前t节点,并且当前t节点的左节点存在,则t指向该左节点;如果新增节点等于当前节点,则新增节点覆盖当前节点。知道找到每个节点的左右节点不存在,将新节点添加为该节点的子节点。
- 在put方法中调用乐fixAfterinsertion()方法用来修复二叉树,前面提到红黑树的定义,但我们发现在put方法中没有任何语句设置红黑色,而且插入操作会导致树不在符合红黑树特征。
private void fixAfterInsertion(Entryx) { x.color = RED; // 直到 x 节点的父节点不是根,且 x 的父节点不是红色 while (x != null && x != root && x.parent.color == RED) { // 如果 x 的父节点是其父节点的左子节点 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { // 获取 x 的父节点的兄弟节点 Entry y = rightOf(parentOf(parentOf(x))); // 如果 x 的父节点的兄弟节点是红色 if (colorOf(y) == RED) { // 将 x 的父节点设为黑色 setColor(parentOf(x), BLACK); // 将 x 的父节点的兄弟节点设为黑色 setColor(y, BLACK); // 将 x 的父节点的父节点设为红色 setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } // 如果 x 的父节点的兄弟节点是黑色 else { // 如果 x 是其父节点的右子节点 if (x == rightOf(parentOf(x))) { // 将 x 的父节点设为 x x = parentOf(x); rotateLeft(x); } // 把 x 的父节点设为黑色 setColor(parentOf(x), BLACK); // 把 x 的父节点的父节点设为红色 setColor(parentOf(parentOf(x)), RED); rotateRight(parentOf(parentOf(x))); } } // 如果 x 的父节点是其父节点的右子节点 else { // 获取 x 的父节点的兄弟节点 Entry y = leftOf(parentOf(parentOf(x))); // 如果 x 的父节点的兄弟节点是红色 if (colorOf(y) == RED) { // 将 x 的父节点设为黑色。 setColor(parentOf(x), BLACK); // 将 x 的父节点的兄弟节点设为黑色 setColor(y, BLACK); // 将 x 的父节点的父节点设为红色 setColor(parentOf(parentOf(x)), RED); // 将 x 设为 x 的父节点的节点 x = parentOf(parentOf(x)); } // 如果 x 的父节点的兄弟节点是黑色 else { // 如果 x 是其父节点的左子节点 if (x == leftOf(parentOf(x))) { // 将 x 的父节点设为 x x = parentOf(x); rotateRight(x); } // 把 x 的父节点设为黑色 setColor(parentOf(x), BLACK); // 把 x 的父节点的父节点设为红色 setColor(parentOf(parentOf(x)), RED); rotateLeft(parentOf(parentOf(x))); } } } // 将根节点设为黑色 root.color = BLACK; }
以上代码注释来源于
自我总结
- 这次二叉树的六个实验中,第二个实验给了我很大的提醒,关于递归思想的掌握度不够高,而红黑树的定义还需要更加深入的了解,源码的分析应该不止局限于那几个方法,还应该加深。网上很多人都对红黑树原理进行乐解析,要多加了解。