13.8 平衡二叉树
如果给定一个数列{1, 2, 3, 4, 5, 6}, 我们生成二叉排序树的话,我们不难看出来,所有结点都是其父节点的右子节点,就像一条链表一样,那样,我们的查询效率将受到影响,甚至不如链表(因为我们要额外判断左子节点是否为空)
基本介绍:
- 平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree) 又被称为 AVL树,可以保证 查询效率
- 特点是:他是一棵空树或者它的做哟两个字数的高度的绝对值不超过1,并且左右两个字数都是一个平衡二叉树。平衡二叉树的常用使用方法又 红黑树、AVL、替罪羊树、Treap、伸展树等
当右子树的高度 - 左子树的高度 > 1 时,我们使用左旋转,意思是,让根节点变换成右子节点,然后,将原来右子节点的左子树,放到原来根节点的左子树的右子树上。 步骤如下:
左旋转:
- 创建一个新的结点
newNode
,使这个新的结点的值等于当前根节点的值 - 把新节点的左子树设置成当前节点的左子树
newNode.left = left
- 把新节点右子树设置成当前节点的右子树的左子树
newNode.right = root.right.left
- 把当前当前结点值换成右子节点的值
value = right.value
- 把当前结点的左子树设置成新节点
right = right.right
left = newLeft
右旋转:
- 创建一个新的结点
newNode
,另这个新的结点的值等于当前结点的值 - 把新节点的右节点设置成当前结点的右节点
newNode.right = right
- 把新节点的左节点设置成当前节点的左节点的右节点
newNode.left = left.right
- 将当前节点的值设置成左节点的值
value = left.value
- 把当前节点的右子树设置成新节点
right = newNode
- 把当前节点的左子树设置成左子树的左子树
left = left.left
package avl;
public class AVLTreeDemo {
public static void main(String[] args) {
int[] arr = {4, 3, 6, 5, 7, 8};
// 创建一个 ACLTree对象
AVLTree avlTree = new AVLTree();
// 添加结点
for (int i = 0; i < arr.length; i++){
avlTree.add(new Node(arr[i]));
}
// 遍历
System.out.println("中序遍历");
avlTree.infixOrder();
System.out.println("平衡~~");
System.out.println("树的高度="+ avlTree.getRoot().height());
System.out.println("树的左子树的高度="+avlTree.getRoot().left.height());
System.out.println("树的右子树的高度="+avlTree.getRoot().right.height());
System.out.println("当前的根节点="+avlTree.getRoot().toString());
}
}
class AVLTree{
private Node root;
public Node getRoot() {
return root;
}
// 添加节点
public void add(Node node){
if (root == null){
root = node;
}else{
root.add(node);
}
}
// 中序遍历
public void infixOrder(){
if (root != null){
root.infixOrder();
} else {
System.out.println("当前二叉排序树为空,不能遍历");
return;
}
}
}
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
// 返回以当前结点为根节点的树的高度
public int height(){
return Math.max(left == null ? 0 : left.height(), right == null ? 0 :right.height()) + 1;
}
// 返回左子树的高度
public int leftHeight(){
if (left == null){
return 0;
}
return left.height();
}
// 返回右子树的高度
public int rightHeight(){
if (right == null){
return 0;
}
return right.height();
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
// 递归添加,添加节点
public void add(Node node){
if (node == null){
return;
}
// 判断传入的节点的值和当前子树的根节点的值的关系
if (node.value < this.value){
// 如果当前节点的左子树为空
if (this.left == null){
this.left = node;
} else {
// 递归的向左子树添加
this.left.add(node);
}
} else {
if (this.right == null){
this.right = node;
} else {
this.right.add(node);
}
}
// 当添加完一个结点后,如果 右子树的高度 -左子树的高度 > 1
if (rightHeight() - leftHeight() > 1){
// 如果右子树的左子树的高度比右子树的左子树的高度要高,先进行一次右旋转
if (right != null && right.leftHeight() > right.rightHeight()){
rightRotate();
}
leftRotate(); // 左旋转
return; // 要办然他还要进行一次判断,防止再给转回去
}
// 当添加完一个节点后,如果 左子树的高度 - 右子树的高度 > 1
if (leftHeight() - rightHeight() > 1){
// 如果左子树的右子树的高度比左子树的左子树的高度高,则先进性一次左旋转
if (left != null && left.rightHeight() > left.leftHeight()){
leftRotate();
}
rightRotate();
}
}
// 中序遍历
public void infixOrder(){
if (this.left != null){
this.left.infixOrder();
}
System.out.print(this.value+"\t");
if (this.right != null){
this.right.infixOrder();
}
}
// 左旋转的方法
private void leftRotate(){
// 创建新的节点,以当前根节点的值
Node newNode = new Node(value);
// 把新的结点的左子树,设置成当前结点的左子树
newNode.left = left;
// 把新的结点右子树设置成当前结点的右子树的左子树
newNode.right = right.left;
// 把当前结点的值替换成右子树的值
value = right.value;
// 把当前结点右子树设置成当前结点右子树的右子树
right = right.right;
// 把当前结点的左子树(左子节点)设置成新的结点
left = newNode;
}
// 右旋转
private void rightRotate(){
// 创建新的节点,以当前根节点的值
Node newNode = new Node(value);
// 把新节点右子树设置成当前结点的右子树
newNode.right = right;
// 把新节点的左子树这支撑当前结点的左子树的右子树
newNode.left = left.right;
// 把当前结点的值替换成左子树的值
value = left.value;
// 把当前结点的左子树设置成左子树的左子树
left = left.left;
// 把当前结点的右子树设置成新结点的右子树
right = newNode;
}
}