java - 数据结构- 平衡二叉树(AVL树)

java

平衡二叉树(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;

}

}

}

以上是 java - 数据结构- 平衡二叉树(AVL树) 的全部内容, 来源链接: utcz.com/z/389623.html

回到顶部