8.二叉树
8.1概述
二叉树是一种基本的非线性数据结构,它是由n(n>=0)个节点构成的有限集合。在二叉树中,每个节点最多有两个子节点,通常被称作左孩子(left child)和右孩子(right child)。此外,二叉树还具有以下特点: 每个节点包含一个值(也可以包含其他信息)。 有一个被称为根(root)的特殊节点,它是二叉树的起点,没有父节点。 如果一个节点存在左子节点,则该节点称为左子节点的父节点;同样,如果存在右子节点,则称为右子节点的父节点。 每个节点的所有子孙(包括子节点、孙子节点等)构成了该节点的子树(subtree)。 左子树和右子树本身也是二叉树,且可以为空。
8.2 二叉树遍历
遍历:
广度优先遍历(Breadth-First Search, BFS)和深度优先遍历(Depth-First Search, DFS)是两种在图或树这类非线性数据结构中搜索节点的常用策略。
广度优先遍历(BFS): 从根节点开始,首先访问所有与根节点直接相连的节点(即第一层邻居节点),然后按顺序访问它们的子节点(第二层),接着是孙子节点(第三层),以此类推。 使用队列作为辅助数据结构,将起始节点入队,每次从队列头部取出一个节点进行访问,并将其未被访问过的相邻节点全部加入队列尾部,直到队列为空为止。 在二叉树场景下,BFS通常实现为层序遍历,它会按照从上到下、从左到右的顺序依次访问各个节点。
深度优先遍历(DFS): 从根节点开始,尽可能深地探索图或树的分支,直到到达叶子节点或者无法继续深入时返回上一层节点,再尝试探索其他分支。 深度优先遍历有多种方式:前序遍历(先访问根节点,再遍历左子树,最后遍历右子树)、中序遍历(先遍历左子树,再访问根节点,最后遍历右子树)、后序遍历(先遍历左子树,再遍历右子树,最后访问根节点)以及反向的前后序遍历等。 在二叉树的DFS中,通常使用递归的方式实现。另外,也可以借助栈这一数据结构,模拟递归过程进行非递归实现。 总结来说,BFS保证了同一层次的节点会被一起访问到,而DFS则是沿着一条路径尽可能深地探索,直至无法继续前进时回溯至另一条路径。这两种遍历方式在解决不同的问题时各有优势,如寻找最短路径、拓扑排序等场景常常会用到BFS,而解决迷宫求解、判断连通性等问题时DFS则更为常见。
8.3 深度优先遍历
深度优先遍历(DFS)分为:
前序遍历(Preorder Traversal): 在前序遍历中,访问顺序为:根节点 -> 左子树 -> 右子树。 递归地对当前节点的左子树进行前序遍历。 递归地对当前节点的右子树进行前序遍历。
中序遍历(Inorder Traversal): 在中序遍历中,访问顺序为:左子树 -> 根节点 -> 右子树。 递归地对当前节点的左子树进行中序遍历。 访问当前节点。 递归地对当前节点的右子树进行中序遍历。
后序遍历(Postorder Traversal): 在后序遍历中,访问顺序为:左子树 -> 右子树 -> 根节点。 递归地对当前节点的左子树进行后序遍历。 递归地对当前节点的右子树进行后序遍历。 访问当前节点。
8.3.1 递归实现遍历
public class TreeTraversal { public static void main(String[] args) { TreeNode tree = new TreeNode( new TreeNode(new TreeNode(4), 2, null), 1, new TreeNode(new TreeNode(5), 3, new TreeNode(6))); preOrder(tree); System.out.println(); inOrder(tree); System.out.println(); postOrder(tree); System.out.println(); } /* * 前序遍历 根节点=》左子树=》右子树 * */ //递归实现 static void preOrder(TreeNode treeNode){ if(treeNode==null){ return; } System.out.print(treeNode.val+"\t");//根节点 preOrder(treeNode.left);//左子树 preOrder(treeNode.right);//右子树 } /* * 中序遍历 左子树=》=》根节点=》右子树 * */ static void inOrder(TreeNode treeNode){ if(treeNode==null){ return; } inOrder(treeNode.left);//左子树 System.out.print(treeNode.val+"\t");//根节点 inOrder(treeNode.right);//右子树 } /* * 后序遍历 左子树=》右子树 =》根节点 * */ static void postOrder(TreeNode treeNode){ if(treeNode==null){ return; } postOrder(treeNode.left);//左子树 postOrder(treeNode.right);//右子树 System.out.print(treeNode.val+"\t");//根节点 } }
8.3.2 非递归实现遍历
//非递归实现 public class TreeTraversal2 { public static void main(String[] args) { TreeNode tree = new TreeNode( new TreeNode(new TreeNode(4), 2, null), 1, new TreeNode(new TreeNode(5), 3, new TreeNode(6))); preOrder(tree); System.out.println(); inOrder(tree); System.out.println(); postOrder(tree); System.out.println(); } /* * 前序遍历 根节点=》左子树=》右子树 * */ static void preOrder(TreeNode treeNode){ LinkedList<TreeNode> stack = new LinkedList<>(); //定义一个指针,当前节点 TreeNode curr = treeNode; //去的路径为前序遍历,回的路径为中序遍历 while (curr != null||!stack.isEmpty()) { if (curr != null) { stack.push(curr);//压入栈,为了记住返回的路径 System.out.print("前序" + curr.val + "\t"); curr = curr.left; }else { TreeNode peek = stack.peek(); TreeNode pop=stack.pop(); curr= pop.right; } } } /* * 中序遍历 左子树=》=》根节点=》右子树 * */ static void inOrder(TreeNode treeNode){ LinkedList<TreeNode> stack = new LinkedList<>(); //定义一个指针,当前节点 TreeNode curr = treeNode; //去的路径为前序遍历,回的路径为中序遍历 while (curr != null||!stack.isEmpty()) { if (curr != null) { stack.push(curr);//压入栈,为了记住返回的路径 curr = curr.left; }else { TreeNode peek = stack.peek(); TreeNode pop=stack.pop(); System.out.print("中序" + pop.val + "\t"); curr= pop.right; } } } /* * 后序遍历 左子树=》右子树 =》根节点 * */ static void postOrder(TreeNode treeNode){ LinkedList<TreeNode> stack = new LinkedList<>(); //定义一个指针,当前节点 TreeNode curr = treeNode; //去的路径为前序遍历,回的路径为中序遍历 TreeNode pop = null; while (curr != null || !stack.isEmpty()) { if (curr != null) { stack.push(curr);//压入栈,为了记住返回的路径 curr = curr.left; } else { TreeNode peek = stack.peek(); //右子树为null,或者最近一次弹栈的元素与当前元素的右子树相等 说明全部子树都处理完了 if (peek.right == null||peek.right==pop) { //最近一次弹栈的元素 pop= stack.pop(); System.out.print("后序" + pop.val + "\t"); } else { curr = peek.right; } } } } }
8.3.2 非递归实现遍历2
//非递归实现 public class TreeTraversal3 { public static void main(String[] args) { TreeNode tree = new TreeNode( new TreeNode(new TreeNode(4), 2, null), 1, new TreeNode(new TreeNode(5), 3, new TreeNode(6))); postOrder(tree); System.out.println(); } static void postOrder(TreeNode treeNode){ LinkedList<TreeNode> stack = new LinkedList<>(); //定义一个指针,当前节点 TreeNode curr = treeNode; //去的路径为前序遍历,回的路径为中序遍历 TreeNode pop = null; while (curr != null || !stack.isEmpty()) { if (curr != null) { //压入栈,为了记住返回的路径 stack.push(curr); //待处理左子树 System.out.println("前序"+curr.val); curr = curr.left; } else { TreeNode peek = stack.peek(); //右子树为null,无需处理 if (peek.right == null) { //最近一次弹栈的元素 System.out.println("中序"+peek.val); pop= stack.pop(); System.out.println("后序"+pop.val); }else if(peek.right==pop) {//最近一次弹栈的元素与当前元素的右子树相等 说明右子树都处理完了 pop= stack.pop(); System.out.println("后序"+pop.val); }else { //待处理右子树 System.out.println("中序"+peek.val); curr = peek.right; } } } } }
9.二叉搜索树
9.1 概述
二叉搜索树(Binary Search Tree,BST)是一种特殊的二叉树数据结构,其设计目的是为了支持高效的查找、插入和删除操作。在二叉搜索树中,每个节点都具有以下性质:
节点值的排序性:
节点的左子树(如果存在)包含的所有节点的值都小于该节点的值。
节点的右子树(如果存在)包含的所有节点的值都大于该节点的值。
递归定义:一个空树是二叉搜索树。
如果一个非空树的根节点满足上述排序性,并且它的左子树和右子树分别都是二叉搜索树,则这个树整体上也是一个二叉搜索树。
操作特性:查找:通过比较待查找值与当前节点值来决定是向左子树还是右子树进行查找,可以将查找时间减少到对数级别(O(log n)),在最优情况下。
插入:新插入的节点根据其值被放到合适的位置以保持树的排序性质。
删除:删除节点可能涉及替换、移动或重新连接节点以维持树的完整性和排序规则。
遍历顺序:中序遍历(Inorder Traversal):对于二叉搜索树来说,中序遍历的结果是一个递增排序的序列。
由于这些特点,二叉搜索树在计算机科学中广泛应用,特别是在需要频繁查询和更新动态集合的情况下,如数据库索引和文件系统等。
9.2 get方法实现
9.2.1 普通get方法
public class BSTree1 { BSTNode root;//根节点 public BSTree1(BSTNode root) { this.root = root; } public BSTree1() { } //根据key查找相应的值 非递归实现 public Object get(int key) { BSTNode node=root; while (node!=null){ if(node.key>key){ node=node.left; } else if (node.key<key) { node=node.right; }else{ return node.value; } } return null; } //根据key查找相应的值 递归实现 /*public Object get(int key) { return get(root, key); } public Object get(BSTNode node, int key) { if (node == null) { return null; } if (node.key > key) { return get(node.left, key); } else if (node.key < key) { return get(node.right, key); } else { return node.value; } }*/ static class BSTNode { int key; Object value; BSTNode left; BSTNode right; public BSTNode(int key) { this.key = key; } public BSTNode(int key, Object value) { this.key = key; this.value = value; } public BSTNode(int key, Object value, BSTNode left, BSTNode right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.2.2 泛型key,value
只要具备比较大小功能的类都可以作为key,而具备比较大小功能只需要继承Comparable接口即可,value也可以用泛型来指定。
public class BSTree2<T extends Comparable<T>,V> { BSTNode<T,V> root;//根节点 public BSTree2(BSTNode root) { this.root = root; } public BSTree2() { } //泛型key 优化,接口必须继承Comparable public V get(T key){ BSTNode<T,V> node=root; while (node!=null){ int res = node.key.compareTo(key); if(res>0){ node=node.left; } else if (res<0) { node=node.right; }else{ return node.value; } } return null; } static class BSTNode<T,V> { T key; V value; BSTNode<T,V>left; BSTNode<T,V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T,V> left, BSTNode<T,V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.3 min,max方法实现
public class BSTree3<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree3(BSTNode root) { this.root = root; } public BSTree3() { } //非递归实现 public V min() { BSTNode<T, V> curr = root; if(curr ==null) return null; while (curr.left != null) { curr = curr.left; } return curr.value; } public V max() { BSTNode<T, V> curr = root; if(curr ==null) return null; while (curr.right != null) { curr = curr.right; } return curr.value; } //递归实现 /*public V min(){ return min(root); } public V min(BSTNode<T,V> node){ if(node ==null) return null; if(node.left!=null){ return min(node.left); }else{ return node.value; } } public V max(){ return max(root); } public V max(BSTNode<T,V> node){ if(node ==null) return null; if(node.right!=null){ return max(node.right); }else{ return node.value; } }*/ static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.4 put方法实现
//put public class BSTree4<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree4(BSTNode root) { this.root = root; } public BSTree4() { } /* * 有该节点,更新value,没有则新增 * */ public void put(T key,V value){ BSTNode<T,V>node=root; BSTNode<T,V>parent=node; while (node!=null){ parent=node; int res = node.key.compareTo(key); if(res>0){ node=node.left; } else if (res<0) { node=node.right; }else{ node.value=value; return; } } //没找到新增节点 BSTNode<T,V>newNode=new BSTNode<>(key,value); //树为空 if(parent==null){ root=newNode; return; } int res = parent.key.compareTo(key); if(res>0){ parent.left=newNode; }else if(res<0){ parent.right=newNode; } } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.5 前任 后继方法实现
对于一般的二叉搜索树来说,其特性是左子树的所有节点值都小于根节点,右子树的所有节点值都大于根节点。 我们首先需要找到目标节点。
然后从目标节点开始回溯到其父节点,如果目标节点是其父节点的左孩子,则其“前任”就是其父节点;
如果不是(即目标节点是其父节点的右孩子),则继续向上回溯至其父节点的父节点,重复此过程。
如果在回溯过程中,我们到达了根节点且根节点是目标节点的父节点,并且目标节点是其右孩子,那么说明目标节点没有“前任”。
前任:
如果目标有左子树,此时前任就是左子树的最大值。
如果目标没有左子树,离他最近的自左而来的祖先就是他的前任。
后继:
如果目标有右子树,此时前任就是右子树的最小值。
如果目标没有右子树,离他最近的自右而来的祖先就是他的前任。
//前任后继 public class BSTree5<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree5(BSTNode root) { this.root = root; } public BSTree5() { } /* * 如果目标有左子树,此时前任就是左子树的最大值。 * 如果目标没有左子树,离他最近的自左而来的祖先就是他的前任。 * */ public V successor(T key) { BSTNode<T, V> curr = root; BSTNode<T, V> leftAncestor = null; while (curr != null) { int res = key.compareTo(curr.key); if (res < 0) { curr = curr.left; } else if (res > 0) { leftAncestor=curr; curr = curr.right; } else { //curr为查找到的节点 break; } } //未找到 if(curr==null){ return null; } //有左子树 if(curr.left!=null){ curr=curr.left; while (curr.right!=null){ curr=curr.right; } return curr.value; } //节点没有左子树 return leftAncestor==null?null:leftAncestor.value; } /* * 如果目标有右子树,此时前任就是右子树的最大值。 * 如果目标没有右子树,离他最近的自右而来的祖先就是他的前任。 * */ public V predecessor(T key){ BSTNode<T, V> curr = root; BSTNode<T, V> rightAncestor = null; while (curr != null) { int res = key.compareTo(curr.key); if (res < 0) { rightAncestor=curr; curr = curr.left; } else if (res > 0) { curr = curr.right; } else { //curr为查找到的节点 break; } } //未找到 if(curr==null){ return null; } //有左子树 if(curr.right!=null){ curr=curr.right; while (curr.left!=null){ curr=curr.left; } return curr.value; } //节点没有左子树 return rightAncestor==null?null:rightAncestor.value; } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.6 删除方法实现
1.删除没有子节点的节点:如果要删除的节点没有子节点,那么可以直接删除该节点,并将其父节点指向该节点的引用设置为 None。
2.删除只有一个子节点的节点:如果要删除的节点只有一个子节点(左或右)。
2.1 删除元素为父元素左子结点:子节点作为父元素的左子结点。
2.2 删除元素为父元素右子结点:子节点作为父元素的右子结点。
3.删除有两个子节点的节点:让被删除节点的后继节点顶替当前节点,分为两种
3.1 后继节点为删除节点的直接子节点:直接顶替删除节点即可。
3.2 后继节点非删除节点的直接子节点:要将后任节点的子节点交给后继节点的父节点,然后用后继节点顶替被删除节点。
9.6.1 非递归实现
//删除节点 public class BSTree6<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree6(BSTNode root) { this.root = root; } public BSTree6() { } public V delete(T key) { BSTNode<T, V> curr = root; // 父节点 BSTNode<T, V> parent = null; while (curr != null) { int res = key.compareTo(curr.key); if (res < 0) { parent = curr; curr = curr.left; } else if (res > 0) { parent = curr; curr = curr.right; } else { //curr为查找到的节点 break; } } //未找到 if (curr == null) { return null; } //删除操作 if (curr.left == null) { //有右子节点,没有左子节点 将元素托管给父元素 shift(parent, curr, curr.right); } else if (curr.left != null) { //有左子节点,没有右子节点 将元素托管给父元素 shift(parent, curr, curr.left); } else { //左右子节点都存在 // 1.查找被删除节点后继节点 BSTNode<T, V> proNode = curr.right; BSTNode<T, V> sParent = curr; while (proNode != null) { //保存父节点信息 sParent = proNode; //找到右子树最小值,没有left属性的元素 proNode = proNode.left; } // 2.处理被删除节点后继节点的情况后 // 2.1继节点为被删除元素的直接子节点(直接替换) // 2.2继节点为被删除元素的非直接子节点 将后任节点的子节点交给后继节点的父节点 if (sParent != curr) { //删除后继节点并将后继节点右侧节点托管给后继节点的父节点(因为查找后继节点的操作,不断像left查找,不可能存在左侧节点) shift(sParent, proNode, proNode.right); //还要将被删除节点的右侧节点托管给后继节点 proNode.right=curr.right; } // 3.用后继节点替换被删除节点(删除当前节点将后继节点托管给父节点) shift(parent, curr, proNode); //将被删除节点的左侧节点托管给后继节点 proNode.left = curr.left; } return curr.value; } private void shift(BSTNode<T, V> parent, BSTNode<T, V> curr, BSTNode<T, V> child) { if (parent == null) { //被删除节点没有父元素 root = curr; } else if (curr == parent.left) { //删除节点为父节点的左孩子 parent.left = child; } else { //删除节点为父节点的右孩子 parent.right = child; } } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.6.2 递归实现
//删除节点 非递归 public class BSTree6<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree6(BSTNode root) { this.root = root; } public BSTree6() { } public V delete(T key) { ArrayList<V> arrayList = new ArrayList<>(); root = delete(key, root, arrayList); return arrayList.isEmpty() ? null : arrayList.get(0); } private BSTNode delete(T key, BSTNode<T, V> node, ArrayList<V> arrayList) { if (node == null) { return null; } int res = key.compareTo(node.key); if (res < 0) { //当前节点的左侧子元素更新为删除结束后剩下的元素 node.left = delete(key, node.left, arrayList); return node; } if (res > 0) { node.right = delete(key, node.right, arrayList); return node; } arrayList.add(node.value); //只有右子元素 if (node.left == null) { return node.right; } //只有左子元素 if (node.right == null) { return node.left; } //有两个子元素 //删除节点与后继节点相邻 BSTNode<T, V> s = node.left; BSTNode<T, V> sParent = node; while (s.left != node) { s = s.left; } //删除节点与后继节点不相邻 s.right = delete(s.key, node.right, new ArrayList<>()); //更新后继节点的左子元素为删除节点的左子元素 s.left = node.left; return s; } private void shift(BSTNode<T, V> parent, BSTNode<T, V> curr, BSTNode<T, V> child) { if (parent == null) { //被删除节点没有父元素 root = curr; } else if (curr == parent.left) { //删除节点为父节点的左孩子 parent.left = child; } else { //删除节点为父节点的右孩子 parent.right = child; } } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
9.7 范围查询
利用中序遍历,从小到大遍历,将符合条件的值添加到List中:
//范围查询 public class BSTree8<T extends Comparable<T>, V> { BSTNode<T, V> root;//根节点 public BSTree8(BSTNode root) { this.root = root; } public BSTree8() { } /* * 中序遍历可以得到由小到大的结果 * 判断与key的关系添加到list内 * 将list返回 * */ //查找小于key public ArrayList<V> less(T key){ ArrayList<V>res=new ArrayList<>(); BSTNode<T,V>p=root; LinkedList<BSTNode<T,V>> stack=new LinkedList(); while (p!=null || !stack.isEmpty()){ if(p!=null){ //未走到最左 stack.push(p); p=p.left; }else{ //到头之后弹栈 BSTNode<T, V> pop = stack.pop(); //处理值 int popRes = pop.key.compareTo(key); if(popRes<0){ res.add(pop.value); }else { break; } //弹出之后走右侧节点 p=pop.right; } } return res; } //查找大于key public ArrayList<V> greater(T key){ ArrayList<V>res=new ArrayList<>(); BSTNode<T,V>p=root; LinkedList<BSTNode<T,V>> stack=new LinkedList(); while (p!=null || !stack.isEmpty()){ if(p!=null){ //未走到最左 stack.push(p); p=p.left; }else{ //到头之后弹栈 BSTNode<T, V> pop = stack.pop(); //处理值 int popRes = pop.key.compareTo(key); if(popRes>0){ res.add(pop.value); } //弹出之后走右侧节点 p=pop.right; } } return res; } //查找大于等于key1,小于等于key2 public ArrayList<V> between(T key1,T key2){ ArrayList<V>res=new ArrayList<>(); BSTNode<T,V>p=root; LinkedList<BSTNode<T,V>> stack=new LinkedList(); while (p!=null || !stack.isEmpty()){ if(p!=null){ //未走到最左 stack.push(p); p=p.left; }else{ //到头之后弹栈 BSTNode<T, V> pop = stack.pop(); //处理值 int popRes1 = pop.key.compareTo(key1); int popRes2 = pop.key.compareTo(key2); if(popRes1>0 && popRes2<0){ res.add(pop.value); } //弹出之后走右侧节点 p=pop.right; } } return res; } static class BSTNode<T, V> { T key; V value; BSTNode<T, V> left; BSTNode<T, V> right; public BSTNode(T key) { this.key = key; } public BSTNode(T key, V value) { this.key = key; this.value = value; } public BSTNode(T key, V value, BSTNode<T, V> left, BSTNode<T, V> right) { this.key = key; this.value = value; this.left = left; this.right = right; } } }
10.AVL树
AVL 树(平衡二叉搜索树)
二叉搜索树在插入和删除时,节点可能失衡
如果在插入和删除时通过旋转,始终让二叉搜索树保持平衡,称为自平衡的二叉搜索树
AVL是自平衡二又搜索树的实现之一如果一个节点的左右孩子,高度差超过1,则此节点失衡,才需要旋转。
10.1 获取高度
//处理节点高度 private int haight(AVLNode node) { return node == null ? 0 : node.height; }
10.2更新高度
//增、删、旋转更新节点高度 //最大高度+1 public void updateHeight(AVLNode treeNode) { treeNode.height=Integer.max(haight(treeNode.left),haight(treeNode.right))+1; }
10.1 旋转
10.1.1 平衡因子
平衡因子:一个节点左子树高度-右子树高度所得结果
小于1平衡:(0,1,-1)
大于1不平衡
>1:左边高
<-1:右边高
public int bf(AVLNode avlNode){ return haight(avlNode.left)-haight(avlNode.right); }
10.1.2 四种失衡情况
LL -失衡节点的 bf >1,即左边更高 -失衡节点的左孩子的bf>=0即左孩子这边也是左边更高或等 一次右旋可以恢复平衡 LR -失衡节点的 bf >1,即左边更高 -失衡节点的左孩子的bf<0 即左孩子这边是右边更高 左子节点先左旋,将树修正为LL情况, 然后失衡节点再右旋,恢复平衡 RL -失衡节点的 bf <-1,即右边更高 -失衡节点的右孩子的bf>0,即右孩子这边左边更高 右子节点先右旋,将树修正为RR情况, 然后失衡节点再左旋,恢复平衡 RR -失衡节点的 bf<-1,即右边更高 -失衡节点的右孩子的bf<=0,即右孩子这边右边更高或等高 一次左旋可以恢复平衡//右旋 /** * @Description 返回旋转后的节点 * @Param [avlNode] 需要旋转的节点 **/ private AVLNode rightRotate(AVLNode redNode) { AVLNode yellowNode = redNode.left; /* *黄色节点有右子节点,给右子节点重新找父级节点 *由于二叉搜索树特性,某个节点的左子节点必定小于 本身,右子节点必定大于本身 * 故:yelllow的右子节点必定大于yellow且小于rea, * 所以,gree可以作为red的左子结点 * */ /* AVLNode greeNode = yellowNode.right; redNode.left = greeNode; */ redNode.left = yellowNode.right; yellowNode.right = redNode; //更新高度,红色和黄色都会改变 updateHeight(redNode); updateHeight(yellowNode); return yellowNode; } //左旋 private AVLNode leftRotate(AVLNode redNode) { //左旋和右旋操作相反 AVLNode yellowNode = redNode.right; //处理yellow的子节点 redNode.right = yellowNode.left; //yellow变为跟,原red比yellow小,为yellow的left yellowNode.left=redNode; updateHeight(redNode); updateHeight(yellowNode); return yellowNode; } /* * LR -失衡节点的 bf >1,即左边更高 -失衡节点的左孩子的bf<0 即左孩子这边是右边更高 左子节点先左旋,将树修正为LL情况, 然后失衡节点再右旋,恢复平衡 * */ //先左旋再右旋 private AVLNode leftRightRotate(AVLNode node) { //修正左子结点LL node.left = leftRotate(node.left); return rightRotate(node); } /* * RL -失衡节点的 bf <-1,即右边更高 -失衡节点的右孩子的bf>0,即右孩子这边左边更高 右子节点先右旋,将树修正为RR情况, 然后失衡节点再左旋,恢复平衡 * */ //先右旋再左旋 private AVLNode rightLeftRotate(AVLNode node) { //修正右子节点为RR node.right= rightRotate(node.left); return leftRotate(node); }
10.1.3 返回一个平衡后的树
//检查节点是否失衡,重新平衡 private AVLNode checkBalance(AVLNode node) { if (node == null) { return null; } //获取该节点的平衡因子 int bf = bf(node); if (bf > 1) { //左边更高的两种情况根据 左子树平衡因子判定 int leftBf = bf(node.left); if (leftBf >= 0) { //左子树左边高 LL 右旋 考虑到删除时等于0也应该右旋 return rightRotate(node); } else { //左子树右边更高 LR return leftRightRotate(node); } } else if (bf < -1) { int rightBf = bf(node.right); if (rightBf <= 0) { //右子树左边更高 RR 虑到删除时等于0也要左旋 return leftRotate(node); } else { //右子树右边更高 RL return rightLeftRotate(node); } } return node; }