/* * 文 件 名: AVLTree.java * 修改时间: 2012-11-30 */ package tree; /** * AVL树 * * @version [版本号, 2012-11-30] */ public class AVLTree { /** * AVL树的根节点 */ private AVLNode root; /** * <默认构造函数> */ public AVLTree() { root = null; } /** * 判断树是否为空 * * @return * @see [类、类#方法、类#成员] */ public boolean isEmpty() { return root == null; } /** * 查找指定关键字的插入位置,小于插左边,大于或者等于插右边 * * @return * @see [类、类#方法、类#成员] */ public AVLNode findInsert(int data) { AVLNode current = root; while (current != null) { if (data < current.data) { if (current.left == null) { break; } else { current = current.left; } } else { if (current.right == null) { break; } else { current = current.right; } } } return current; } /** * AVL树插入操作,最重要的操作 * * @param data * @see [类、类#方法、类#成员] */ public void insert(int data) { // 新建一个节点 AVLNode insertNode = new AVLNode(data); // 如果根节点为空,直接赋值给根节点 if (root == null) { root = insertNode; return; } AVLNode insertPos = root; // 先将节点插入树中,需要回朔改变插入路径的各个节点的平衡因子。 while (insertPos != null) { if (data < insertPos.data) { if (insertPos.left == null) { insertPos.left = insertNode; insertPos.bf += 1; insertNode.parent = insertPos; break; } else { insertPos = insertPos.left; } } else { if (insertPos.right == null) { insertPos.right = insertNode; insertPos.bf -= 1; insertNode.parent = insertPos; break; } else { insertPos = insertPos.right; } } } // 下面是回朔算法,从insertPos节点开始检查 AVLNode current = insertPos; while (true) { // 如果节点的平衡因子为0,不需要再回朔了,直接返回 if (current.bf == 0) { return; } // 如果节点的平衡因子为1或者-1,首先修正父节点的平衡因子,但是要先判断当前节点是否是root else if (current.bf == 1 || current.bf == -1) { // 如果已经回朔到根节点,则直接返回 if (current == root) { return; } // 如果插入的节点在当前节点的父节点的左边,则将父节点的bf值+1 else if (data < current.parent.data) { current.parent.bf += 1; } // 否则当前节点的父节点的bf值减一 else { current.parent.bf -= 1; } // 继续回朔当前的父节点 current = current.parent; } // 如果节点的平衡因子的绝对值大于1,则说明找到了最小不平衡子树的根,下面左旋转修正操作 else if (current.bf == 2 || current.bf == -2) { // 比较一下data数据的大小即可知道插入的是左边还是右边了。。。 // /首先确定是什么类型的旋转RR,LL,RL,LR // LL型,插在当前节点的左子树的左子树,因为data小于当前节点的data, // 所以当前节点的左子树必不为空,即有两个节点位置需要调整,current、current.left // A-current // / // B-current.left // / // C 需要将current绕着B节点右旋,bf值改变的节点为A和B if (data < current.data && data < current.left.data) { // 这里需要分两种情况,一种是当前节点为根节点,一种是非根节点 // 还要再分两种情况,一种是current.left有右节点,一种是current.left没有右节点 // 这里有个疑问,如何在调整完节点位置之后修正节点的bf值 // 如果当前节点为根节点,即父节点为null rightRotate(current); } // RR类型旋转 else if (data >= current.data && data >= current.right.data) { leftRotate(current); } // LR型旋转,先要将current.left绕着current.left.right左旋,再将current绕着current.left右旋 else if (data < current.data && data >= current.left.data) { leftRotate(current.left); rightRotate(current); } else { rightRotate(current.right); leftRotate(current); } // 如果B的bf值为0或者B为根节点,则跳出循环 if (current.parent == root || current.parent.bf == 0) { break; } // 否则继续检查B的父节点 else { current = current.parent.parent; } } } } /** * AVL树删除操作,首先找到删除的节点,如果左子树不为空,则将待删除节点的数据和左子树中data最大的节点的数据交换,否则如果右子树不为空, * 则将待删除节点与右子树中最小的节点交换数据,实际删除的都是叶子节点,然后回朔修改bf值 * * @return */ public AVLNode delete(int data) { AVLNode current = root; while (current != null) { if (data < current.data) { current = current.left; } else if (data > current.data) { current = current.right; } else {// 找到该节点了,下面是找到实际的叶子节点与该节点交换 // 如果该节点的左子树不为空,则将左子树的最大节点与该节点交换 int temp = current.data; AVLNode tempNode = null; if (current.left != null) { tempNode = maxNode(current.left); } else if (current.right != null) { tempNode = minNode(current.right); } if (tempNode != null) { current.data = tempNode.data; tempNode.data = temp; current = tempNode; } // 下面是具体的删除操作了,其实就是删除叶子节点了 if (current == root) { root = null; return current; } else if (current == current.parent.left) {// 如果当前节点是其父节点的左子树 if (current.left != null) {// 如果当前节点的左子树不为空 current.parent.left = current.left; current.left.parent = current.parent; } else if (current.right != null) { current.parent.left = current.right; current.right.parent = current.parent; } else { current.parent.left = null; } current.parent.bf -= 1; } else {// 如果当前节点是其父节点的右子树 if (current.left != null) {// 如果当前节点的左子树不为空,则将当前节点的左子树链接到当前节点的父节点 current.parent.right = current.left; current.left.parent = current.parent; } else if (current.right != null) { current.parent.right = current.right; current.right.parent = current.parent; } else { current.parent.right = null; } current.parent.bf += 1; } tempNode = current.parent;// 这里用上面的tempNode节点作为回朔的开端,省的定义太多变量很乱 // 下面是删除操作的回朔算法 while (true) { // 如果节点的bf为1或者-1,说明删除之前的bf为0(因为删除之前是平衡的,不可能为2或者-2情况),删除之后树的高度没有改变,不需要再回朔判断 if (tempNode.bf == 1 || tempNode.bf == -1) { return current; } else if (tempNode.bf == 0) {// 如果节点的bf为0,说明删除之前bf为1或者-1,删除之后高度减少了,那么需要首先判断当前节点是父节点的左子树还是右子树,再回朔父节点 // 首先需要判断是否已经回朔到了根节点 if (tempNode == root) { return current; } else if (tempNode == tempNode.parent.left) {// 如果当前节点是父节点的左子树,说明父节点的左子树高度下降了 tempNode.parent.bf -= 1; } else { tempNode.parent.bf += 1; } } else {// 如果节点的bf为2或者-2,则需要通过旋转保持平衡,首先确定旋转的类型,bf值大于等于0向左,小于0向右 if (tempNode.bf >= 0 && tempNode.left.bf >= 0) {// LL型旋转 rightRotate(tempNode); } else if (tempNode.bf >= 0 && tempNode.left.bf < 0) {// LR型旋转 leftRotate(tempNode.left); rightRotate(tempNode); } else if (tempNode.bf < 0 && tempNode.right.bf >= 0) {// RL型旋转 rightRotate(tempNode.right); leftRotate(tempNode); } else {// RR型旋转 leftRotate(tempNode); } } tempNode = tempNode.parent; } } } return current; } /** * 返回一个树的最大值节点 * * @param root * @return */ private AVLNode maxNode(AVLNode root) { AVLNode current = root; while (current != null) { if (current.right == null) { break; } else { current = current.right; } } return current; } /** * 返回一个树的最小值节点 * * @param root * @return */ private AVLNode minNode(AVLNode root) { AVLNode current = root; while (current != null) { if (current.left == null) { break; } else { current = current.left; } } return current; } /** * 将节点左旋的方法 * * @param node * @see [类、类#方法、类#成员] */ private void leftRotate(AVLNode node) { // 旋转代码 if (node.parent == null) { root = node.right; } else if (node == node.parent.left) { node.parent.left = node.right; } else { node.parent.right = node.right; } node.right.parent = node.parent; if (node.right.left != null) { node.right.left.parent = node; } node.parent = node.right; node.right = node.parent.left; node.parent.left = node; // 调整平衡因子 if (node.parent.bf < 0)//如果父节点的平衡因子小于0 { node.bf = node.bf - node.parent.bf + 1; } else //如果父节点的平衡因子大于或等于0 { node.bf += 1; } if (node.bf < 0) { node.parent.bf += 1; } else { node.parent.bf = node.parent.bf + node.bf + 1; } } /** * 将节点右旋的方法 * * @param node * @see [类、类#方法、类#成员] */ private void rightRotate(AVLNode node) { if (node.parent == null) { root = node.left; } else if (node == node.parent.left) { node.parent.left = node.left; } else { node.parent.right = node.left; } // 将B的父节点设为A的父节点 node.left.parent = node.parent; // 如果B的右子树不为null,则将B的右子树的父节点设为A if (node.left.right != null) { node.left.right.parent = node; } // 将A的父节点设为B node.parent = node.left; // 将B的右子树接到A的左边 node.left = node.parent.right; node.parent.right = node; // 求得当前节点旋转后的bf值,这个是经过公式推导出来的会有三种情况-1,0,1 // node.bf = node.bf - node.parent.bf - 1; // if (node.bf == -1) // { // node.parent.bf -= 2; // } // else // { // node.parent.bf -= 1; // } if (node.parent.bf >= 0) { node.bf = node.bf - node.parent.bf - 1; } else { node.bf -= 1; } if (node.bf >= 0) { node.parent.bf -= 1; } else { node.parent.bf = node.parent.bf + node.bf - 1; } } /** * 中序遍历二叉树 * * @see [类、类#方法、类#成员] */ public void middleDisplay() { middleDisplay(root); } /** * 使用递归方式中序遍历二叉树 * * @param root * @see [类、类#方法、类#成员] */ private void middleDisplay(AVLNode root) { if (root != null) { middleDisplay(root.left); root.displayNode(); middleDisplay(root.right); } } }
AVL树java实现代码
最新推荐文章于 2022-06-13 21:47:54 发布