1.二叉排序树可能存在的问题
给你一个数列{1,2,3,4,5,6},要求创建一颗二叉排序树(BST,如下图
可以看到这是二叉排序树存在许多的问题,那怎么解决呢?用平衡二叉树(AVL)来解决.
2.平衡二叉树基本介绍
(1)平衡二叉树也叫做平衡二叉搜索树(Self-balancing binary search tree)又称为AVL树,可以保证查询效率较高。
(2)具有以下特点:它是一颗空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。
(3)举例说明,看看下面哪些是AVL树。
3.平衡二叉树的左旋转
(1)要求:给您一个数列,创建处对应的平衡二叉树,数列{4,3,6,5,7,8}
(2)示意图:
(3)代码实现:
//左旋转
public void leftRotate() {
//1.创建新的节点,以当前根节点的值
Node newNode = new Node(this.value);
//2.把新节点的左子树设置为当前节点的左子树
newNode.left = this.left;
//3.把新节点的右子树设置成为当前节点的右子树的左子树
newNode.right = this.left.right;
//4.把当前节点的值换为右子节点的值
this.value = this.right.value;
//5.把当前节点的右子树设置成当前节点的右子树的右子树
this.right = this.right.right;
//6.把当前节点的左子树设置为新节点
this.left = newNode;
}
4.平衡二叉树的右旋转
(1)要求;给你一个数列,创建对应的平衡二叉树,数列{10,12,8,9,7,6}
(2)示意图:
(3)代码实现
//右旋转
public void rightRotate() {
//1.创建一个新的节点newNode(以根节点的值10创建),创建一个新的节点,值等于当前根节点的值,new Node(this.value)
Node newNode = new Node(this.value);
//2.把新节点的右子树设置了当前节点的右子树,newNode.right=this.right
newNode.right = this.right;
//3.把新节点的左子树设置成当前节点的左子树的右子树,newNode.left = this.left.right
newNode.left = this.left.right;
//4.把当前节点的值换成当前节点左子节点的值,this.value = this.left.value
this.value = this.left.value;
//5.把当前节点的左子树设置成当前节点的左子树的左子树,this.left = this.left.left
this.left = this.left.left;
//6.把当前节点的右子树设置成新节点,this.right=newNode
this.right = newNode;
}
5.平衡二叉树完整代码
class AvlTree {
private Node root;
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
//创建二叉排序树
public void add(Node node) {
if (root == null) {
root = node;
return;
}
root.addNode(node);
//当添加完一个节点后,如果右子树的高度-左子树的高度 > 1 且当前节点的右子节点的右子树高度大于右子节点的左子树高度,则左旋转
if (this.root.getRightHigh() - this.root.getLeftHigh() > 1 && this.root.getRight().getRightHigh() > this.root.getRight().getLeftHigh()) {
this.root.leftRotate();
}
//当添加完一个节点后,如果右子树的高度-左子树的高度 > 1 且当前节点的右子节点的右子树高度大于右子节点的左子树高度,则左旋转
if (this.root.getLeftHigh() - this.root.getRightHigh() > 1 && this.root.getLeft().getLeftHigh()> this.root.getRight().getRightHigh()) {
this.root.rightRotate();
}
}
//中序遍历
public void infixOrder() {
if (root != null) {
this.root.infixOrder();
return;
}
throw new NullPointerException("根节点为空");
}
}
//二叉排序树的节点
class Node {
private int value;
private Node left;
private Node right;
public Node(int value) {
this.value = value;
}
public Node() {
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
//中序遍历
public void infixOrder() {
if (this.left != null) {
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null) {
this.right.infixOrder();
}
}
/**
* 向二叉排序树中添加节点
*
* @param node 要添加的节点
*/
public void addNode(Node node) {
if (node == null) {
throw new NullPointerException("节点为空");
}
//判断当前节点的值是否大于添加节点
if (this.value > node.value) {
//判断当前节点的左子节点是否为空
if (this.left == null) {
this.left = node;
return;
}
//递归
this.left.addNode(node);
return;
}
//判断当前节点的右子节点是否为空
if (this.right == null) {
this.right = node;
return;
}
//递归
this.right.addNode(node);
}
//返回当前节点的高度
public int getTreeHigh() {
return Math.max(this.left == null ? 0 : this.left.getTreeHigh(), this.right == null ? 0 : this.right.getTreeHigh()) + 1;
}
//返回左子树的高度
public int getLeftHigh() {
if (this.left == null) {
return 0;
}
return this.left.getTreeHigh();
}
//返回右子树的高度
public int getRightHigh() {
if (this.right == null) {
return 0;
}
return this.right.getTreeHigh();
}
//左旋转
public void leftRotate() {
//1.创建新的节点,以当前根节点的值
Node newNode = new Node(this.value);
//2.把新节点的左子树设置为当前节点的左子树
newNode.left = this.left;
//3.把新节点的右子树设置成为当前节点的右子树的左子树
newNode.right = this.left.right;
//4.把当前节点的值换为右子节点的值
this.value = this.right.value;
//5.把当前节点的右子树设置成当前节点的右子树的右子树
this.right = this.right.right;
//6.把当前节点的左子树设置为新节点
this.left = newNode;
}
//右旋转
public void rightRotate() {
//1.创建一个新的节点newNode(以根节点的值10创建),创建一个新的节点,值等于当前根节点的值,new Node(this.value)
Node newNode = new Node(this.value);
//2.把新节点的右子树设置了当前节点的右子树,newNode.right=this.right
newNode.right = this.right;
//3.把新节点的左子树设置成当前节点的左子树的右子树,newNode.left = this.left.right
newNode.left = this.left.right;
//4.把当前节点的值换成当前节点左子节点的值,this.value = this.left.value
this.value = this.left.value;
//5.把当前节点的左子树设置成当前节点的左子树的左子树,this.left = this.left.left
this.left = this.left.left;
//6.把当前节点的右子树设置成新节点,this.right=newNode
this.right = newNode;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}
6.平衡二叉树的双旋转
前面两个数列,进行单旋转(记一次旋转)就可以将非平衡二叉树转成平衡二叉树,但是再某些情况下,单旋转不能完成平衡二叉树的转换。比如
int[] array={10,11,7,6,8,9}或者int[] array={2,1,6,5,7,3},进过一次旋转,非平衡二叉树并没有转为平衡二叉树。
问题分析:
改进之后的代码:
public void add(Node node) {
if (root == null) {
root = node;
return;
}
root.addNode(node);
//当添加完一个节点后,如果右子树的高度-左子树的高度 > 1 ,则进行左旋转
if (this.root.getRightHigh() - this.root.getLeftHigh() > 1 ) {
//当前节点的右子节点的左子树高度大于右子节点的右子树高度,则先对当前节点的右子节点进行右旋转
if (this.root.getRight().getRightHigh() < this.root.getRight().getLeftHigh()) {
this.root.getRight().rightRotate();
//然后再对当前节点进行左旋转
this.root.leftRotate();
return;
}
this.root.leftRotate();
}
//当添加完一个节点后,如果左子树的高度-右子树的高度 > 1 ,则进行右旋转
if (this.root.getLeftHigh() - this.root.getRightHigh() > 1 ) {
//当前节点的左子节点的右子树高度大于左子节点的左子树高度,则先对当前节点的左子节点进行左旋转
if (this.root.getLeft().getLeftHigh() < this.root.getLeft().getRightHigh()) {
this.root.getLeft().leftRotate();
//然后再对当前节点进行右旋转
this.root.rightRotate();
return;
}
this.root.rightRotate();
}
}