平衡二叉树(Balanced Binary Tree), 一般也叫做AVL树
AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis(我才知道。。。)
平衡二叉树是一种特殊的二叉排序树,它的任意节点的左子树和右子树的深度之差不超过1.
以此防止左右深度差距过大,导致的查询困难。 (变成平衡二叉树后查询起来就类似于二分查询了)
而其中的难点主要是左旋,右旋,和双旋
首先推荐一个超级好用的数据结构网站= =
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
可以动态演示各种数据结构
再添加完节点后,判断(写在添加方法中,所以会从新节点往上递归对每一个经过的节点进行检查)节点情况
1. 如果一个节点A的左子树高度 - 右子树高度 > 1, A的左子树的右子树高度 > A 的左子树的左子树高度
先对A的左子树左旋,然后对A右旋
2. 如果一个节点A的左子树高度 - 右子树高度 > 1, 且不满足上面那种情况
只对A右旋
3.如果一个节点A的右子树高度 - 左子树高度 > 1, A的右子树的左子树高度 > A的右子树的右子树高度
先对A的右子树右旋,然后对A左旋
4.如果一个节点A的右子树高度 - 左子树高度 > 1, 且不满足上面那种情况
只对A左旋
怎么说呢= =|||反正我从来不看别人的代码,看着都头晕。。。真想搞明白还是自己敲一遍练练吧。。。
package tree; public class AVLTree { Node root; public static void main(String args[]){ //左旋 // AVLTree avlTree1 = new AVLTree(); // avlTree1.addNode(2); // avlTree1.addNode(1); // avlTree1.addNode(4); // avlTree1.addNode(3); // avlTree1.addNode(5); // avlTree1.addNode(6); // avlTree1.show(); // System.out.println(); // System.out.println(avlTree1.root.nodeHeight()); // System.out.println(avlTree1.root.left.nodeHeight()); // System.out.println(avlTree1.root.right.nodeHeight()); //右旋 // AVLTree avlTree2 = new AVLTree(); // avlTree2.addNode(5); // avlTree2.addNode(3); // avlTree2.addNode(6); // avlTree2.addNode(2); // avlTree2.addNode(4); // avlTree2.addNode(1); // avlTree2.show(); // System.out.println(); // System.out.println(avlTree2.root.nodeHeight()); // System.out.println(avlTree2.root.left.nodeHeight()); // System.out.println(avlTree2.root.right.nodeHeight()); //右边高度 - 左边高度 > 1 //左孩子的右孩子 > 左边的左孩子 //先左子树左旋转,然后本节点右旋转 // AVLTree avlTree3 = new AVLTree(); // avlTree3.addNode(5); // avlTree3.addNode(2); // avlTree3.addNode(6); // avlTree3.addNode(1); // avlTree3.addNode(3); // avlTree3.addNode(4); // // avlTree3.show(); // System.out.println(); // System.out.println("此时根节点为:" + avlTree3.root.value); // System.out.println("此时根节点左孩子为:" + avlTree3.root.left.value); // System.out.println("此时根节点右孩子为:" + avlTree3.root.right.value); // // System.out.println(avlTree3.root.nodeHeight()); // System.out.println(avlTree3.root.left.nodeHeight()); // System.out.println(avlTree3.root.right.nodeHeight()); //左边高度 - 右边高度 > 1 //右孩子的左孩子 > 右边的右孩子 //先右子树右旋转,然后本节点左旋转 AVLTree avlTree4 = new AVLTree(); avlTree4.addNode(5); avlTree4.addNode(2); avlTree4.addNode(8); avlTree4.addNode(7); avlTree4.addNode(9); avlTree4.addNode(6); avlTree4.show(); System.out.println(); System.out.println("此时根节点为:" + avlTree4.root.value); System.out.println("此时根节点左孩子为:" + avlTree4.root.left.value); System.out.println("此时根节点右孩子为:" + avlTree4.root.right.value); System.out.println(avlTree4.root.nodeHeight()); System.out.println(avlTree4.root.left.nodeHeight()); System.out.println(avlTree4.root.right.nodeHeight()); } public AVLTree(){ this.root = null; } //添加节点 public void addNode(int num){ if(root == null){ root = new Node(num); } else{ this.root.addNode(num); } } //中序遍历 (按顺序从小到大) public void show(){ if(root == null){ System.out.println("树为空"); } else{ root.show(); } } public class Node{ public int value; public Node left; public Node right; public Node(int num){ this.value = num; } //添加节点 public void addNode(int num){ if(num < this.value){ if(this.left == null) { this.left = new Node(num); } else{ this.left.addNode(num); }; } else if(num >= this.value){ if(this.right == null) { this.right = new Node(num); } else{ this.right.addNode(num); }; } //判断是否需要旋转 ,这样递归的时候就可以从底向上依次判断左右高度 int leftHeight = this.left == null ? 0 : this.left.nodeHeight(); int rightHeight = this.right == null ? 0 : this.right.nodeHeight(); if(rightHeight - leftHeight > 1){ //此时分两种情况 //如果当前节点右孩子的左孩子高度 > 右孩子的右孩子高度,需要先左旋转,然后右旋转。 //否则只左旋转 int rightLeftHeight = this.right.left == null ? 0 : this.right.left.nodeHeight(); int rightRightHeight = this.right.right == null ? 0 : this.right.right.nodeHeight(); if(rightLeftHeight > rightRightHeight ){ System.out.println("新加节点" + num + "后需要先对右子树右旋转然后左旋转"); this.right.rightRotate(); this.leftRotate(); } else{ System.out.println("新加节点" + num + "后需要左旋转"); this.leftRotate(); } } if(leftHeight - rightHeight > 1){ //此时分两种情况 //如果当前节点的左子树的右子树高度 > 左子树的左子树高度,需要先右转然后左转 //否则只需要右转 int leftLeftHeight = this.left.left == null ? 0 : this.left.left.nodeHeight(); int leftRightHeight = this.left.right == null ? 0 : this.left.right.nodeHeight(); if(leftRightHeight > leftLeftHeight){ System.out.println("新加节点" + num + "后需要先对左子树左旋转然后右旋转"); this.left.leftRotate(); this.rightRotate(); } else { System.out.println("新加节点" + num + "后需要右旋转"); this.rightRotate(); } } } //中序遍历 public void show(){ if(this.left != null){ this.left.show(); } System.out.print(this.value + " "); if(this.right != null){ this.right.show(); } } //添加节点 //1.先把节点挂在对应位置,然后判断根节点左右两边的最大深度。 //所以先咬写判断深度的算法。 //返回左子树高度 public int nodeHeight(){ // if(this.left != null && this.right == null){ // return this.left.nodeHeight() + 1; // } // else if(this.left == null && this.right != null){ // return this.right.nodeHeight() + 1; // } // else if(this.left == null && this.right == null){ // return 1; // } // else { // return Math.max(this.left.nodeHeight(), this.right.nodeHeight()) + 1; // } return Math.max( this.left == null ? 0 : left.nodeHeight(), //当left == null时, 左边深度为0, 不等于0时, 左边深度为 left.nodeHeight() this.right == null ? 0 : this.right.nodeHeight() //当right == null时, 左边深度为0, 不等于0时, 左边深度为 right.nodeHeight() ) + 1; } //以当前节点为基准左旋 (当前节点左边和右边高度不一样) public void leftRotate(){ //对 A 左旋时 // 1.创建一个新节点,.把 A 的值赋给 新节点 // 2.把A的左子树设为新节点的左子树,把新节点放在A的左子树, // 3.把A的右子树的左子树设为A的右子树 // 4. 把A的右子树的值赋给A,把A的右子树的右子树给A作为新的右子树 //这样避免了找父亲的过程。 // 2 2 4 4 // 1 4 -> 2 4 -> 2 4 -> 2 5 // 3 5 1 3 5 1 3 5 1 3 6 // 6 6 6 Node newNode = new Node(this.value); newNode.left = this.left; this.left = newNode; newNode.right = this.right.left; this.value = this.right.value; this.right =this.right.right; } //以当前节点为基准右旋 (当前节点左边和右边高度不一样) public void rightRotate(){ //对 A 右旋时 // 1.创建一个新节点,.把 A 的值赋给 新节点 // 2.把A的右子树设为新节点的右子树,把新节点放在A的右子树, // 3.把A的左子树的右子树设为新节点的左子树 // 4. 把A的左子树的值赋给A,把A的左子树的左子树给A作为新的左子树 //和左旋正好镜像,把所有的左右互换就可以了-。- // 5 5 5 3 // 3 6 -> 3 5 -> 3 5 -> 2 5 // 2 4 2 4 6 2 4 6 1 4 6 // 1 1 1 Node newNode = new Node(this.value); newNode.right = this.right; this.right = newNode; newNode.left = this.left.right; this.value = this.left.value; this.left = this.left.left; } } }