前文中,咱们实现了二叉查找树,同时,咱们也提到了这么一个隐患:若是二叉树没法控制本身的深度,那么,二叉树的查找效率极可能会发生极端的转化--如,顺序的将一堆数据插入查找二叉树,此时,二叉树会成为一个近似链表的数据结构。因此,为了解决这个问题,咱们必需要找到问题的根源所在--二叉树深度,由此,推出本文的主角--平衡二叉树。java
定义:平衡二叉树,要求左右子树的深度差异不超过1,且左右子树都是平衡二叉树。(二叉树的定义老是充满了递归味)
git
平衡二叉树的实现方式:平衡二叉树有多种实现方式,最多见的就有AVL树以及红黑二叉树,AVL树是最先被发明的平衡二叉树,敬它是前辈,本文先说AVL树的实现。
github
定义在上头已经说过,我们直接看看AVL树如何实现自平衡的吧(所谓自平衡,就是在节点发生修改的时候,本身完成树平衡的调整)。
数据结构
示例:
code
假设有以下四种树:
blog
要说不平衡的状况其实变幻无穷,好比左边深度为一万,右边深度为0,可是咱们考虑的是,在平衡树中修改一个节点之后,发生的不平衡“事件”,因此,左右的差只能为2。
递归
总结:其实旋转的本质,是让不平衡的更深一端,替换根节点,下降其深度,这时,更深端的子树就变成原根节点,如何处理原更深端的子树,就成了最大问题。如左左,右右,都是有一个能符合要求的叶子节点,能直接完成叶子的剪切。左右和右左,暴露出来的叶子都不能完成剪切,因此须要对(左或者右)节点来一次旋转,让上层的树有一个符合要求的叶子。事件
不一样于普通的二叉查找树,二叉平衡树,须要关注节点的高度,因此须要在数据结构中,加入节点高度的封装。同时,加入左旋转,右旋转,左右旋转,右左旋转的方法,并在删除节点,或者插入节点这类发生节点变更的状况,判断是否树不平衡,以及是哪一种状况的不平衡,再处理旋转。Talk is cheap,咱们直接看代码吧。get
左旋转代码:
it
//当为“左左”状况的时候,发生的单旋转 public void singleRollLeft() { //将临时右子树设置为root Tree rightTree = root; //获取root的左子树 Tree leftTree = root.getlChild(); //获取原左子树的右叶子 Tree rightOne = leftTree.getrChild(); //将root置为左子树 root = leftTree; //将右子树的左节点变为原左子树的右叶子 rightTree.setlChild(rightOne); root.setrChild(rightTree); //高度调整,因为只有原来的左子树和原来的父节点发生了变动,因此只修改两个节点的高度 root.getrChild().height = max(root.getrChild().getlChild().height,root.getrChild().getrChild().height)+1; root.height = max(root.getlChild().height,root.getrChild().height)+1; }右旋转代码:
//当为“右右”状况的时候,发生的单旋转 public void singleRollRight() { //将临时左子树设置为root Tree leftTree = root; //获取root的右子树 Tree rightTree = root.getrChild(); //获取原右子树的左叶子 Tree leftOne = rightTree.getlChild(); //将root置为右子树 root = rightTree; //将左子树的右节点变为原右子树的左叶子 leftTree.setrChild(leftOne); root.setlChild(leftTree); //高度调整 root.getlChild().height = max(root.getlChild().getlChild().height,root.getlChild().getrChild().height)+1; root.height = max(root.getlChild().height,root.getrChild().height)+1; }左右旋转:
//当碰到左右的状况 public void doubleRollLR() { TreeUtil leftRoot = new TreeUtil(root.getlChild()); leftRoot.singleRollRight(); singleRollLeft(); }右左旋转:
//当碰到右左的状况 public void doubleRollRL() { TreeUtil rightRoot = new TreeUtil(root.getrChild()); rightRoot.singleRollLeft(); singleRollRight(); }插入:
//插入 public void insert(int value) { if(root == null) { root = new Tree(); root.setValue(value); } else if(value == root.getValue()) { return; } else if(value > root.getValue()){ TreeUtil rightRoot = new TreeUtil(root.getrChild()); rightRoot.insert(value); root.setrChild(rightRoot.getRoot()); int leftHeight = 0; int rightHeight = 0; if(root.getrChild() != null) { rightHeight = root.getrChild().height; } if(root.getlChild() != null) { leftHeight = root.getlChild().height; } if(leftHeight+ 2 == rightHeight ) { if(value < root.getrChild().getValue()) { doubleRollRL(); } else{ singleRollRight(); } } } else { TreeUtil leftRoot = new TreeUtil(root.getlChild()); leftRoot.insert(value); root.setlChild(leftRoot.getRoot()); int leftHeight = 0; int rightHeight = 0; if(root.getrChild() != null) { rightHeight = root.getrChild().height; } if(root.getlChild() != null) { leftHeight = root.getlChild().height; } if(rightHeight+ 2 == leftHeight ) { if(value > root.getlChild().getValue()) { doubleRollLR(); } else{ singleRollLeft(); } } } }删除:
//删除 public void delete(int value) { if(root == null) { return; } if(value > root.getValue()) { TreeUtil rightRoot = new TreeUtil(root.getrChild()); rightRoot.delete(value); root.setrChild(rightRoot.getRoot()); int leftHeight = 0; int rightHeight = 0; if(root.getrChild() != null) { rightHeight = root.getrChild().height; } if(root.getlChild() != null) { leftHeight = root.getlChild().height; } if(rightHeight+ 2 == leftHeight ) { if(value > root.getlChild().getValue()) { doubleRollLR(); } else{ singleRollLeft(); } } } else if(value < root.getValue()) { TreeUtil leftRoot = new TreeUtil(root.getlChild()); leftRoot.delete(value); root.setlChild(leftRoot.getRoot()); int leftHeight = 0; int rightHeight = 0; if(root.getrChild() != null) { rightHeight = root.getrChild().height; } if(root.getlChild() != null) { leftHeight = root.getlChild().height; } if(leftHeight+ 2 == rightHeight ) { if(value < root.getrChild().getValue()) { doubleRollRL(); } else{ singleRollRight(); } } } else { //判断删除节点是否存在左子树 boolean hasLeft = false; //判断删除节点是否存在右子树 boolean hasRight = false; if(root.getlChild() != null) { hasLeft = true; } if(root.getrChild() != null) { hasRight = true; } if(!hasLeft && !hasRight)//不包含左子树以及右子树 { root = null; } else if(!hasLeft)//只包含右子树 { root = root.getrChild(); } else if (!hasRight) { root = root.getlChild(); } else { Tree rollTree = root.getrChild(); while(rollTree.getlChild() != null) { rollTree = rollTree.getlChild(); } root.setValue(rollTree.getValue()); TreeUtil rightRoot = new TreeUtil(root.getrChild()); rightRoot.delete(rollTree.getValue()); root.setrChild(rightRoot.getRoot()); } } }
完整代码git路径:https://github.com/liufangqi/treeTestRealOne
----------
我的再总结:核心理解四种不平衡的状态,左左,右右,左右,右左,对应着“更深”端的位置。左左能够直接单次左旋转完成平衡,右右能够单次右旋转完成平衡,左右,须要将左子树右旋转,再左旋转,右左,须要将右子树左旋转,再右旋转。