2.二叉查找树不只具备二叉树的操做,还具备如下的特殊操做:html
public LinkedBinarySearchTree() { super(); } public LinkedBinarySearchTree(T element) { super(element); if (!(element instanceof Comparable)) throw new NonComparableElementException("LinkedBinarySearchTree"); }
(4)若是这个新元素大于或等于树根处存储的那个元素且根的右孩子不是null,则会遍历根的右孩子,并再次进行比较操做。java
public void addElement(T element) { if (!(element instanceof Comparable)) throw new NonComparableElementException("LinkedBinarySearchTree"); Comparable<T> comparableElement = (Comparable<T>)element; if (isEmpty()) root = new BinaryTreeNode<T>(element); else { if (comparableElement.compareTo(root.getElement()) < 0) { if (root.getLeft() == null) this.getRootNode().setLeft(new BinaryTreeNode<T>(element)); else addElement(element, root.getLeft()); } else { if (root.getRight() == null) this.getRootNode().setRight(new BinaryTreeNode<T>(element)); else addElement(element, root.getRight()); } } modCount++; } private void addElement(T element, BinaryTreeNode<T> node) { Comparable<T> comparableElement = (Comparable<T>)element; if (comparableElement.compareTo(node.getElement()) < 0) { if (node.getLeft() == null) node.setLeft(new BinaryTreeNode<T>(element)); else addElement(element, node.getLeft()); } else { if (node.getRight() == null) node.setRight(new BinaryTreeNode<T>(element)); else addElement(element, node.getRight()); } }
public T removeElement(T targetElement) throws ElementNotFoundException { T result = null; if (isEmpty()) throw new ElementNotFoundException("LinkedBinarySearchTree"); else { BinaryTreeNode<T> parent = null; if (((Comparable<T>)targetElement).equals(root.element)) { result = root.element; BinaryTreeNode<T> temp = replacement(root); if (temp == null) root = null; else { root.element = temp.element; root.setRight(temp.right); root.setLeft(temp.left); } modCount--; } else { parent = root; if (((Comparable)targetElement).compareTo(root.element) < 0) result = removeElement(targetElement, root.getLeft(), parent); else result = removeElement(targetElement, root.getRight(), parent); } } return result; } private T removeElement(T targetElement, BinaryTreeNode<T> node, BinaryTreeNode<T> parent) throws ElementNotFoundException { T result = null; if (node == null) throw new ElementNotFoundException("LinkedBinarySearchTree"); else { if (((Comparable<T>)targetElement).equals(node.element)) { result = node.element; BinaryTreeNode<T> temp = replacement(node); if (parent.right == node) parent.right = temp; else parent.left = temp; modCount--; } else { parent = node; if (((Comparable)targetElement).compareTo(node.element) < 0) result = removeElement(targetElement, node.getLeft(), parent); else result = removeElement(targetElement, node.getRight(), parent); } } return result; } private BinaryTreeNode<T> replacement(BinaryTreeNode<T> node) { BinaryTreeNode<T> result = null; if ((node.left == null) && (node.right == null)) result = null; else if ((node.left != null) && (node.right == null)) result = node.left; else if ((node.left == null) && (node.right != null)) result = node.right; else { BinaryTreeNode<T> current = node.right; BinaryTreeNode<T> parent = node; while (current.left != null) { parent = current; current = current.left; } current.left = node.left; if (node.right != current) { parent.left = current.right; current.right = node.right; } result = current; } return result; }
public void removeAllOccurrences(T targetElement) throws ElementNotFoundException { removeElement(targetElement); try { while (contains((T)targetElement)) removeElement(targetElement); } catch (Exception ElementNotFoundException) { } }
public T removeMin() throws EmptyCollectionException { T result = null; if (isEmpty()) throw new EmptyCollectionException("LinkedBinarySearchTree"); else { if (root.left == null) { result = root.element; root = root.right; } else { BinaryTreeNode<T> parent = root; BinaryTreeNode<T> current = root.left; while (current.left != null) { parent = current; current = current.left; } result = current.element; parent.left = current.right; } modCount--; } return result; }
17.AVL树的右旋:由下图可知咱们是在结点T的左结点的左子树上作了插入元素的操做,咱们称这种状况为左左状况,咱们应该进行右旋转(只需旋转一次,故是单旋转)【步骤与右旋步骤同样】node
过程:git
18.AVL树的左旋:由下图可知咱们是在结点T的右结点的右子树上作了插入元素的操做,咱们称这种状况为右右状况,咱们应该进行左旋转(只需旋转一次,故是单旋转)【步骤与左旋步骤同样】数据结构
过程:函数
19.AVL树的左右(先左后右)旋:以下图,只是单纯的进行一次旋转,获得的树仍然是不平衡的。因此应该进行二次旋转。学习
20.AVL树的右左(先右后左)旋:以下图,只是单纯的进行一次旋转,获得的树仍然是不平衡的。因此应该进行二次旋转。this
22.红黑树的元素添加及删除。.net
public Node getSuccessor(Node delNode){ //参数为被删除的节点 //定义一个当前节点的引用,直接让往下走一步,走到被删除节点的右节点 Node curr=delNode.right; Node successor=curr; //用来指向中级后续节点 Node sucParent=null; //用来指向中级后续节点的父节点 while(curr!=null){ sucParent=successor; successor=curr; curr=curr.left; } //循环中止,中级后续节点被找出 if(successor!=delNode.right){ //将后继节点的子节点(只可能有右节点)替补到后继节点的位置上 sucParent.left=successor.right; //将被删除的右孩子链接到后继节点的右节点上 successor.right=delNode.right; //将被删除的左孩子链接到后继节点的右节点上(就是用后继节点替换掉被删除的节点) } return successor; }
问题引伸解决方案:若是直接删结点,整个树的大小顺序就乱了,因此须要考虑,在树中找到一个合适的节点来把这个结点给替换掉,用这种方法来保持整个树的稳定。须要在树中找出全部比被删除节点的值大的全部数,并在这些数中找出一个最小的数来。设计
问题2解决方案:删除时树的平衡性受到破坏,提升它的操做的时间复杂度。而AVL树就不会出现这种状况,树的高度始终是O(lgN).高度越小,对树的一些基本操做的时间复杂度就会越小。
private void insert(RBTNode<T> node) { int cmp; RBTNode<T> y = null; RBTNode<T> x = this.mRoot; // 1. 将红黑树看成一颗二叉查找树,将节点添加到二叉查找树中。 while (x != null) { y = x; cmp = node.key.compareTo(x.key); if (cmp < 0) x = x.left; else x = x.right; } node.parent = y; if (y!=null) { cmp = node.key.compareTo(y.key); if (cmp < 0) y.left = node; else y.right = node; } else { this.mRoot = node; } // 2. 设置节点的颜色为红色 node.color = RED; // 3. 将它从新修正为一颗二叉查找树 insertFixUp(node); } /* * 新建结点(key),并将其插入到红黑树中 * * 参数说明: * key 插入结点的键值 */ public void insert(T key) { RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null); // 若是新建结点失败,则返回。 if (node != null) insert(node); }
/* * 删除结点(node),并返回被删除的结点 * * 参数说明: * node 删除的结点 */ private void remove(RBTNode<T> node) { RBTNode<T> child, parent; boolean color; // 被删除节点的"左右孩子都不为空"的状况。 if ( (node.left!=null) && (node.right!=null) ) { // 被删节点的后继节点。(称为"取代节点") // 用它来取代"被删节点"的位置,而后再将"被删节点"去掉。 RBTNode<T> replace = node; // 获取后继节点 replace = replace.right; while (replace.left != null) replace = replace.left; // "node节点"不是根节点(只有根节点不存在父节点) if (parentOf(node)!=null) { if (parentOf(node).left == node) parentOf(node).left = replace; else parentOf(node).right = replace; } else { // "node节点"是根节点,更新根节点。 this.mRoot = replace; } // child是"取代节点"的右孩子,也是须要"调整的节点"。 // "取代节点"确定不存在左孩子!由于它是一个后继节点。 child = replace.right; parent = parentOf(replace); // 保存"取代节点"的颜色 color = colorOf(replace); // "被删除节点"是"它的后继节点的父节点" if (parent == node) { parent = replace; } else { // child不为空 if (child!=null) setParent(child, parent); parent.left = child; replace.right = node.right; setParent(node.right, replace); } replace.parent = node.parent; replace.color = node.color; replace.left = node.left; node.left.parent = replace; if (color == BLACK) removeFixUp(child, parent); node = null; return ; } if (node.left !=null) { child = node.left; } else { child = node.right; } parent = node.parent; // 保存"取代节点"的颜色 color = node.color; if (child!=null) child.parent = parent; // "node节点"不是根节点 if (parent!=null) { if (parent.left == node) parent.left = child; else parent.right = child; } else { this.mRoot = child; } if (color == BLACK) removeFixUp(child, parent); node = null; } /* * 删除结点(z),并返回被删除的结点 * * 参数说明: * tree 红黑树的根结点 * z 删除的结点 */ public void remove(T key) { RBTNode<T> node; if ((node = search(mRoot, key)) != null) remove(node); }
问题3:按着书上讲的左旋右旋步骤来作,会出现错误
问题代码的过程为下图,很显然出现了覆盖结点,致使丢失结点的问题。
改正:
过程:
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 0/0 | 1/1 | 2/2 | |
第二周 | 1010/1010 | 1/2 | 10/12 | |
第三周 | 651/1661 | 1/3 | 13/25 | |
第四周 | 2205/3866 | 1/4 | 15/40 | |
第五周 | 967/4833 | 2/6 | 22/62 | |
第六周 | 1680/6513 | 1/7 | 34/96 | |
第七周 | 2196/8709 | 1/8 | 35/131 |
计划学习时间:30小时
实际学习时间:35小时
改进状况:AVL树和红黑树真的耗费了大量的时间!