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; }
10.2 新增
public void put(int key, Object value){ doPut(root,key,value); } //找空位并添加新的节点 private AVLNode doPut(AVLNode node, int key, Object value) { /* * 1.找到空位,创建新节点 * 2.key 已存在 更新位置 * 3.继续寻找 * */ //未找到 if (node ==null){ return new AVLNode(key,value); } //找到了节点 重新赋值 if(key ==node.key){ node.value=value; return node; } //继续查找 if(key < node.key){ //继续向左,并建立父子关系 node.left= doPut(node.left,key,value); }else{ //向右找,并建立父子关系 node.right= doPut(node.right,key,value); } //更新节点高度 updateHeight(node); //重新平衡树 return checkBalance(node); }
10.3 删除
//删除 public void remove(int key) { root = doRemove(root, key); } private AVLNode doRemove(AVLNode node, int key) { //1. node==null if (node == null) { return node; } //2.未找到key if (key < node.key) { //未找到key,且key小于当前节点key,继续向左 node.left = doRemove(node.left, key); } else if (key > node.key) { //向右找 node.right = doRemove(node.right, key); } else { //3.找到key if (node.left == null && node.right == null) { //3.1 没有子节点 return null; } else if (node.left != null && node.right == null) { //3.2 一个子节点(左节点) node = node.left; } else if (node.left == null && node.right != null) { //3.2 一个子节点(右节点) node = node.right; } else { //3.3 两个子节点 //找到他最小的后继节点,右子树向左找到底 AVLNode s=node; while (s.left!=null){ s=s.left; } //s即为后继节点,将节点删除并重新修正右子树 s.right=doRemove(s.right,s.key); //左子树为s.left,右子树为原删除节点的左子树 s.left=node.left; node=s; } } //4.更新高度 updateHeight(node); //5.检查是否失衡 return checkBalance(node); }
10.4 整体代码
package org.alogorithm.tree.AVLTree; public class AVLTree { static class AVLNode { int key; int height = 1;//高度初始值1 AVLNode left, right; private AVLNode root; Object value; public AVLNode(int key, Object value) { this.key = key; this.value = value; } public AVLNode(int key, AVLNode left, AVLNode right, AVLNode root) { this.key = key; this.left = left; this.right = right; this.root = root; } } //处理节点高度 private int haight(AVLNode node) { return node == null ? 0 : node.height; } //增、删、旋转更新节点高度 //最大高度+1 public void updateHeight(AVLNode treeNode) { treeNode.height = Integer.max(haight(treeNode.left), haight(treeNode.right)) + 1; } /* 平衡因子:一个节点左子树高度-右子树高度所得结果 小于1平衡:(0,1,-1) 大于1不平衡 >1:左边高 <-1:右边高 */ public int bf(AVLNode avlNode) { return haight(avlNode.left) - haight(avlNode.right); } //四种失衡情况 /* 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); } //检查节点是否失衡,重新平衡 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; } AVLNode root; public void put(int key, Object value) { doPut(root, key, value); } //找空位并添加新的节点 private AVLNode doPut(AVLNode node, int key, Object value) { /* * 1.找到空位,创建新节点 * 2.key 已存在 更新位置 * 3.继续寻找 * */ //未找到 if (node == null) { return new AVLNode(key, value); } //找到了节点 重新赋值 if (key == node.key) { node.value = value; return node; } //继续查找 if (key < node.key) { //继续向左,并建立父子关系 node.left = doPut(node.left, key, value); } else { //向右找,并建立父子关系 node.right = doPut(node.right, key, value); } //更新节点高度 updateHeight(node); //重新平衡树 return checkBalance(node); } //删除 public void remove(int key) { root = doRemove(root, key); } private AVLNode doRemove(AVLNode node, int key) { //1. node==null if (node == null) { return node; } //2.未找到key if (key < node.key) { //未找到key,且key小于当前节点key,继续向左 node.left = doRemove(node.left, key); } else if (key > node.key) { //向右找 node.right = doRemove(node.right, key); } else { //3.找到key if (node.left == null && node.right == null) { //3.1 没有子节点 return null; } else if (node.left != null && node.right == null) { //3.2 一个子节点(左节点) node = node.left; } else if (node.left == null && node.right != null) { //3.2 一个子节点(右节点) node = node.right; } else { //3.3 两个子节点 //找到他最小的后继节点,右子树向左找到底 AVLNode s=node; while (s.left!=null){ s=s.left; } //s即为后继节点,将节点删除并重新修正右子树 s.right=doRemove(s.right,s.key); //左子树为s.left,右子树为原删除节点的左子树 s.left=node.left; node=s; } } //4.更新高度 updateHeight(node); //5.检查是否失衡 return checkBalance(node); } }