平衡二叉树

一、平衡二叉树

平衡二叉树又叫平衡二叉搜索树(Self-balancing Binary Search Tree),又被称为AVL树

平衡二叉树可定义为或者是一棵空树,或者是具有下列性质的二叉树:其左子树和右子树均为平衡二叉树,且左子树和右子树的高度差的绝对值不超过1。注意:平衡二叉树一定是二叉排序树。含有n个结点的平衡二叉树的最大深度为O(log2(n)),即平衡二叉树的平均查找长度为O(log2(n))
如下图所示为一棵平衡二叉树和一棵非平衡二叉树:
在这里插入图片描述
二、平衡二叉树的插入

平衡二叉树的插入过程的前半部分与二叉排序树相同,但在新结点插入后,若造成查找路径上的某个结点不再平衡,则需要做出相应的调整。可将调整的规律归纳为下列四种情况:

(1)RR平衡旋转(左单旋转)

前提条件:右子树的高度减去左子树的高度大于1,即:rightHeight() - leftHeight() > 1

(1)创建一个新的结点newNode,使其值等于当前根结点的值;
(2)将新结点的左子树设置为当前结点的左子树:newNode.left=left
(3)将新结点的右子树设置为当前结点的右子树的左子树:newNode.right =right.left
(4)将当前结点的值换为右子结点的值:value=right.value
(5)将当前结点的右子树设置成右子树的右子树:right=right.right
(6)将当前结点的左子树设置成新结点:left=newNode

示意图:

在这里插入图片描述

private void leftRotate() {
		Node newNode =new Node(value);//创建一个新的结点newNode
		newNode.left=left;//将新结点的左子树设置为当前结点的左子树
		newNode.right=right.left;//将新结点的右子树设置为当前结点的右子树的左子树
		value=right.value;//将当前结点的值换为右子结点的值
		right=right.right;//将当前结点的右子树设置成右子树的右子树
		left=newNode;//将当前结点的左子树设置成新结点
	}

(2)LL平衡旋转(右单旋转)

前提条件:左子树的高度减去右子树的高度大于1,即:leftHeight() - rightHeight() > 1

(1)创建一个新的结点newNode,使其值等于当前根结点的值;
(2)将新结点的右子树设置为当前结点的右子树:newNode.right=right
(3)将新结点的左子树设置为当前结点的左子树的右子树:newNode.left =left.right
(4)将当前结点的值换为左子结点的值:value=left.value
(5)将当前结点的左子树设置成左子树的左子树:left=left.left
(6)将当前结点的右子树设置成新结点:right=newNode

示意图:
在这里插入图片描述

private void rightRotate() {
		Node newNode =new Node(value);//创建一个新的结点newNode
		newNode.right=right;//将新结点的右子树设置为当前结点的右子树
		newNode.left=left.right;//将新结点的左子树设置为当前结点的左子树的右子树
		value=left.value;//将当前结点的值换为左子结点的值
		left=left.left;//将当前结点的左子树设置成左子树的左子树
		right=newNode;//将当前结点的右子树设置成新结点
	}

(3)LR平衡旋转(先左后右双旋转)

前提条件:符合RR平衡旋转(左单旋转)前提下,当前结点的左子树的右子树高度大于它的左子树的左子树的高度,即:left.rightHeight()>left.leftHeight()

(1)先对当前结点的左结点进行左单旋转;
(2)再对当前结点进行右单旋转即可。

if(leftHeight()-rightHeight()>1) {//当添加完一个结点后,如果 :(左子树的高度 - 右子树的高度) > 1, 右旋转
			if(left!=null&&left.rightHeight()>left.leftHeight()) {//若它的左子树的右子树高度大于它的左子树的左子树的高度
				left.leftRotate();//LR平衡旋转(先左后右双旋转)
				rightRotate();
			}else {
				rightRotate();//LL平衡旋转(右单旋转)
			}
		}

(4)RL平衡旋转(先右后左双旋转)

前提条件:符合LL平衡旋转(右单旋转)前提下,当前结点的右子树的左子树的高度大于它的右子树的右子树的高度,即: right.leftHeight()>right.rightHeight()

(1)先对当前结点的右结点进行右单旋转;
(2)再对当前结点进行左单旋转即可。

if(rightHeight()-leftHeight()>1) {//当添加完一个结点后,如果: (右子树的高度-左子树的高度) > 1 ,左旋转
			if(right!=null&&right.leftHeight()>right.rightHeight()) {//若它的右子树的左子树的高度大于它的右子树的右子树的高度
				right.rightRotate();//RL平衡旋转(先右后左双旋转)
				leftRotate();
			}else {
				leftRotate();//RR平衡旋转(左单旋转)
			}
			return;
		}

三、实现完整代码

package Tree;

public class AVLTree {
	public static void main(String[] args) {
		int[] arr= {10,11,7,6,8,9};
		AVL avlTree=new AVL();
		for(int i=0;i<arr.length;i++) {
			 avlTree.add(new Node(arr[i]));
		}
		
		System.out.println("初始平衡二叉树的中序遍历");
		avlTree.inOrder();
		System.out.println("双旋转处理后:");
		avlTree.inOrder();
	    System.out.println("树的高度=" + avlTree.getRoot().height()); // 3
	    System.out.println("树的左子树高度=" + avlTree.getRoot().leftHeight()); // 2
	    System.out.println("树的右子树高度=" + avlTree.getRoot().rightHeight()); // 2
	    System.out.println("当前的根结点=" + avlTree.getRoot());// 8
	    System.out.println("根节点的左结点=" + avlTree.getRoot().left);// 7
	    System.out.println("根节点的右结点=" + avlTree.getRoot().right);// 10
	}
}

class AVL{
	private Node root;
	
	public Node getRoot() {//获取根结点
		return root;
	}
	
	public void add(Node node) {//添加子结点
		if (root == null) {//若根结点为空直接让添加结点成为子结点
			root = node;
		} else {
			root.add(node);
		}
	}
	
	public void inOrder() {//中序遍历
		if(root!=null) {//若根结点不为空则调用结点的inOrder
			root.inOrder();
		}else {
			System.out.println("平衡二叉树为空,无法遍历!");
		}
	}
}

class Node{
	int value;
	Node left;
	Node right;
	
	 public Node(int value) {//Node的构造函数
		 this.value=value;
	 }
	 
	@Override
	public String toString() {//重写toString方法
		return "Node [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) {//左子树为空直接返回0
			return 0;
		}
		return left.height();//递归左子树的高度
	}
	
	public int rightHeight() {//返回右子树的高度
		if(right==null) {//右子树为空直接返回0
			return 0;
		}
		return right.height();//递归右子树的高度
	}
	
	//RR平衡旋转(左单旋转)
	private void leftRotate() {
		Node newNode =new Node(value);//创建一个新的结点newNode
		newNode.left=left;//将新结点的左子树设置为当前结点的左子树
		newNode.right=right.left;//将新结点的右子树设置为当前结点的右子树的左子树
		value=right.value;//将当前结点的值换为右子结点的值
		right=right.right;//将当前结点的右子树设置成右子树的右子树
		left=newNode;//将当前结点的左子树设置成新结点
	}
	
	//LL平衡旋转(右单旋转)
	private void rightRotate() {
		Node newNode =new Node(value);//创建一个新的结点newNode
		newNode.right=right;//将新结点的右子树设置为当前结点的右子树
		newNode.left=left.right;//将新结点的左子树设置为当前结点的左子树的右子树
		value=left.value;//将当前结点的值换为左子结点的值
		left=left.left;//将当前结点的左子树设置成左子树的左子树
		right=newNode;//将当前结点的右子树设置成新结点
	}
	
	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);//否则递归向当前结点的右子树遍历
			}
		}
		
		if(rightHeight()-leftHeight()>1) {//当添加完一个结点后,如果: (右子树的高度-左子树的高度) > 1 ,左旋转
			if(right!=null&&right.leftHeight()>right.rightHeight()) {//若它的右子树的左子树的高度大于它的右子树的右子树的高度
				right.rightRotate();//RL平衡旋转(先右后左双旋转)
				leftRotate();
			}else {
				leftRotate();//RR平衡旋转(左单旋转)
			}
			return;
		}
		
		if(leftHeight()-rightHeight()>1) {//当添加完一个结点后,如果 :(左子树的高度 - 右子树的高度) > 1, 右旋转
			if(left!=null&&left.rightHeight()>left.leftHeight()) {//若它的左子树的右子树高度大于它的左子树的左子树的高度
				left.leftRotate();//LR平衡旋转(先左后右双旋转)
				rightRotate();
			}else {
				rightRotate();//LL平衡旋转(右单旋转)
			}
		}
	}
	
	public void inOrder() {//中序遍历
		if(this.left!=null) {
			this.left.inOrder();
		}
		System.out.println(this);
		if(this.right!=null) {
			this.right.inOrder();
		}
	}
}

运行结果:

初始平衡二叉树的中序遍历
Node [value=6]
Node [value=7]
Node [value=8]
Node [value=9]
Node [value=10]
Node [value=11]
双旋转处理后:
Node [value=6]
Node [value=7]
Node [value=8]
Node [value=9]
Node [value=10]
Node [value=11]
树的高度=3
树的左子树高度=2
树的右子树高度=2
当前的根结点=Node [value=8]
根节点的左结点=Node [value=7]
根节点的右结点=Node [value=10]

该实例的变化过程如下图所示:
在这里插入图片描述

  • 9
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值