平衡二叉树
对于数列{1,2,3,4,5,6},创建一颗普通的二叉排序树,会有一些问题:
- 左子树全部为空,更像是链表而不是树
- 插入同样很快,但查询的效率降低了。
为了解决这个问题,提出了平衡二叉树,实际上是对二叉排序树的优化,可以提高查询的效率。
平衡二叉树的特点是每棵左子树和右子树的高度相差不超过1。
平衡二叉树的实现
实际上,平衡二叉树是通过对二叉排序树旋转来完成的。而旋转的实际意义是更改二叉排序树的根节点,通过二叉排序树的创立规则,数组的第一个数就作为根节点,但这样有时会导致左右子树不平衡,这时就将根节点改为左或右子节点,以此平衡左右子树的高度。
左旋转
当二叉排序树的右子树高度-左子树高度大于1,就进行左旋转。对照上图:
- 创建一个新节点,值为根节点,也就是4。
- 让新节点左边连上根节点的左子节点。连上3
- 让新节点的右边连上根节点右子节点的左子节点。连上5
- 把根节点的值改为它的右子节点值,为6
- 让根节点的右边指向右子节点的右子节点。指向7
- 让根节点的左边指向新节点。指向4
原来的6就会因为没有指向而被回收,整个操作就是把6这个节点提上来作为根节点,平衡左右子树。
右旋转
右旋转是左旋转的镜像。当二叉排序树的左子树高度-右子树高度大于1,就进行右旋转。对照上图:
- 创建一个新节点,值为根节点,也就是10。
- 让新节点右边连上根节点的右子节点。连上12
- 让新节点的左边连上根节点左子节点的右子节点。连上9
- 把根节点的值改为它的左子节点值,为8
- 让根节点的左边指向左子节点的左子节点。指向7
- 让根节点的右边指向新节点。指向10
双旋转
有的时候,只进行一次旋转无法将一棵二叉排序树转化为平衡二叉树,这时候就需要多次旋转。
判断条件:需要进行左旋转时,也就是目标节点的右子树高度-左子树高度大于1时,如果目标节点的右子树的左子树高度大于目标节点的右子树的右子树高度,需要先对目标节点的右子树节点进行右旋转,再对目标节点进行左旋转,否则,直接进行左旋转即可。
同理,需要进行右旋转时,也就是目标节点的左子树高度-右子树高度大于1时,如果目标节点的左子树的右子树高度大于目标节点的左子树的左子树高度,需要先对目标节点的左子树节点进行左旋转,再对目标节点进行右旋转,否则,直接进行右旋转即可。
代码
package avlTree;
public class AVLTreeDemo {
public static void main(String[] args) {
int[] num={2,1,6,5,7,3};
AVLTree avlTree = new AVLTree();
for(int i:num){
avlTree.add(new Node(i));
}
avlTree.midOrder();
System.out.println("---------");
System.out.println("树的深度"+avlTree.root.height());
System.out.println("左子树深度"+avlTree.root.leftHeight());
System.out.println("右子树深度"+avlTree.root.rightHeight());
}
}
class AVLTree{
Node root;
//添加节点
public void add(Node node){
if(root==null){
root=node;
}else {
root.add(node);
}
}
//中序遍历
public void midOrder(){
if(root==null){
System.out.println("空树");
}else {
root.midOrder();
}
}
}
//树节点
class Node{
int value;
Node left;
Node right;
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
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;
}else {
return left.height();
}
}
//该节点右子树的高度
public int rightHeight(){
if(right==null){
return 0;
}else {
return right.height();
}
}
//左旋转
public void leftRotate(){
Node newnode = new Node(value);
newnode.left=left;
newnode.right=right.left;
value=right.value;
right=right.right;
left=newnode;
}
//右旋转
public void rightRotate(){
Node newnode = new Node(value);
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){
if(right!=null&&right.leftHeight()>right.rightHeight()){
right.rightRotate();
leftRotate();
}else {
leftRotate();
}
return;
}
if(leftHeight()-rightHeight()>1){
if(left!=null&&left.rightHeight()>left.leftHeight()){
left.leftRotate();
rightRotate();
}else {
rightRotate();
}
}
}
//中序遍历
public void midOrder(){
if(this.left!=null){
this.left.midOrder();
}
System.out.println(this);
if(this.right!=null){
this.right.midOrder();
}
}
}