引入:看一下案例(说明二叉排序树可能存在的问题)
数列{1,2,3,4,5,6},二叉排序树(BST)的形式为
平衡二叉树基本介绍
(1)平衡二叉树也叫平衡二叉搜索树(Self-balancing binary search tree),又被称为AVL树,可以保证查询效率比较高.
(2)具有以下特点:它是一颗孔数或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树,平衡二叉树的常用实现方法有(这里都是算法)----红黑树,AVL,替罪羊树,Treap,伸展树
下面哪些是AVL树?哪些不是?
应用案例—单旋转(左旋转)
原始二叉树为:
处理步骤(左旋转):因为右子树比较长
(1)创建一个新的结点(以4这个值创建),值等于当前节点的值.
(2)把新节点的左子树设置了当前节点的左子树(3)
(3)把新节点的右子树设置成当前节点的右子树的左子树(5)
(4)把当前节点的值换为右子节点的值(将4替换成6)
(5)把当前节点的右子树设置成右子树的右子树(将6指向7)
(6)把当前节点的左子树设置成新节点(6连接4)
结果如下
//左旋转的方法
public void leftRotate(){
//创建新的结点,以当前的结点为根节点
Node newNode = new Node(value);
//把新结点的左子树设置成当前结点的左子树
newNode.left = left;
//把新结点的右子树设置成当前结点右子树的左子树
newNode.right = right.left;
//把当前结点的值替换成右子结点的值
value = right.value;
//把当前结点的右子树设置成当前结点右子树的右子树
right = right.right;
//把当前结点的左子树(左子结点)设置成新的结点
left = newNode;
}
右旋转
处理步骤:(右旋转)左子树比较长
(1)创建一个新的结点,newNode,值等于当前根结点的值
(2)把新结点的右子树设置成当前结点的右子树
(3)把新结点的左子树设置成当前结点左子树的右子树
(4)把当前结点的值换成左子结点的值
(5)把当前结点的左子树设置成左子树的左子树
(6)把当前结点的右子树设置成新结点
//右旋转的方法
public void rightRotate(){
//(1)创建一个新的结点,newNode,值等于当前根结点的值
Node newNode = new Node(value);
//(2)把新结点的右子树设置成当前结点的右子树
newNode.right = right;
//(3)把新结点的左子树设置成当前结点左子树的右子树
newNode.left = left.right;
//(4)把当前结点的值换成左子结点的值
value = left.value;
//(5)把当前结点的左子树设置成左子树的左子树
left = left.left;
//(6)把当前结点的右子树设置成新结点
right = newNode;
}
问题提出:看下面这张图片
我们发现,这里符合右旋转的条件,但是旋转后依然不是平衡二叉树,所以在旋转之前需要进一步判断:
(1)当符合右旋转的条件时
(2)如果它的左子树的右子树高度大于它的左子树高度时
(3)先对当前这个结点的左节点进行左旋转
(4)然后对当前这个结点进行右旋转
反之亦然
if(rightHeight() - leftHeight() > 1){
//如果它的左子树的右子树高度大于它的左子树高度时
if(right != null && right.leftHeight() > right.rightHeight()){
left.rightRotate();
leftRotate();
}else{
leftRotate();
}
return; //切记不可忘记
}
if(leftHeight() - rightHeight() > 1){
if(left != null && left.rightHeight() >left.leftHeight()){
left.leftRotate();
rightRotate();
}else{
rightRotate();
}
}
附上整个完整代码(包括测试)
package com.self.dataStructure.avlTree;
public class AVLTreeDemo {
public static void main(String[] args) {
//int[] arr = {4,3,6,5,7,8};
//int[] arr = {10,12,8,9,7,6};
int[] arr = {10,11,7,6,8,9};
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
avlTree.add(new Node(arr[i]));
}
System.out.println("前序遍历");
avlTree.infexOrderTree();
System.out.println("没有处理之前");
System.out.println(avlTree.getRoot().height());
System.out.println(avlTree.getRoot().leftHeight());
System.out.println(avlTree.getRoot().rightHeight());
}
}
class AVLTree{
private Node root;
public Node getRoot(){
return root;
}
/**
* @param value 删除的值
* @return 删除值的节点
*/
public Node searchTree(int value){
if(root == null){
return null;
}else{
return root.search(value);
}
}
/**
* @param value 想要删除的值
* @return 删除节点的父节点
*/
public Node searchParentTree(int value){
if(root == null){
return null;
}else{
return root.searchParent(value);
}
}
/**
* @param node 传入的节点(当做二叉排序树的很节点)
* @return 该二叉树的最小节点
*/
public int delRightTreeMin(Node node){
Node target = node;
while (target.left != null){ //循环查找左子节点 就会找到最小值
target = target.left;
}
deleteNode(target.value);
return target.value;
}
/**
* @param value 想要删除的值
*/
public void deleteNode(int value){
if(root == null){
return;
}else{
Node targetNode = searchTree(value);
if(targetNode == null){
return;
}
if(root.left == null && root.right == null){ //该二叉排序树只有一个结点
root = null;
return;
}
Node parentNode = searchParentTree(value);
//如果要删除的是叶子结点
if(targetNode.left == null && targetNode.right == null){
//判断targetNode是parentNode的左子节点还是右子节点
if(parentNode.left != null && parentNode.left.value == value){
parentNode.left = null;
}else if(parentNode.right != null && parentNode.right.value == value){
parentNode.right = null;
}
}else if(targetNode.left != null && targetNode.right != null){
int rightTreeMin = delRightTreeMin(targetNode.right);
targetNode.value = rightTreeMin;
}else{ //删除只有一颗子树的节点
if(targetNode.left != null){ //如果要删除的结点有左子结点
//如果targetNode是parentNode的左子结点
if(parentNode != null){
if(parentNode.left.value == value){
parentNode.left = targetNode.left;
}else if(parentNode.right.value == value){
parentNode.right = targetNode.left;
}
}else{
root = targetNode.left;
}
}else{
if(parentNode != null){
if(parentNode.right.value == value){
parentNode.right = targetNode.right;
}else if(parentNode.left.value == value){
parentNode.left = targetNode.right;
}
}else{
root = targetNode.right;
}
}
}
}
}
public void add(Node node){
if(root == null){
root = node;
}else{
root.add(node);
}
}
public void infexOrderTree(){
if(root != null){
root.infixOrder();
}else{
System.out.println("binarySortTree is null");
}
}
}
class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
//左旋转的方法
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(){
//(1)创建一个新的结点,newNode,值等于当前根结点的值
Node newNode = new Node(value);
//(2)把新结点的右子树设置成当前结点的右子树
newNode.right = right;
//(3)把新结点的左子树设置成当前结点左子树的右子树
newNode.left = left.right;
//(4)把当前结点的值换成左子结点的值
value = left.value;
//(5)把当前结点的左子树设置成左子树的左子树
left = left.left;
//(6)把当前结点的右子树设置成新结点
right = newNode;
}
//返回左子树的高度
public int leftHeight(){
if(left == null){
return 0;
}
return left.height();
}
//返回右子树的高度
public int rightHeight(){
if(right == null){
return 0;
}
return right.height();
}
//返回以该结点为根结点的树高度
public int height(){
return Math.max(left == null ? 0 :left.height(),right == null ? 0 : right.height()) + 1;
}
/**
* @param value 希望删除的值
* @return 如果找到,返回该及节点 否则返回null
*/
public Node search(int value){
if(this.value == value){
return this;
}else if(value < this.value){
if(this.left == null){
return null;
}
return this.left.search(value);
}else{
if(this.right == null){
return null;
}
return this.right.search(value);
}
}
/**
* @param value 想要查找的值
* @return 如果找到,就该节点的父节点 否则返回null
*/
public Node searchParent(int value){
if((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)){
return this;
}else{
if(value < this.value && this.left != null){
return this.left.searchParent(value);
}else if(value >= this.value && this.right != null){
return this.right.searchParent(value);
}else{
return null; //没有找到父结点
}
}
}
public void add(Node node){
if(node == null){
return;
}
if(node.value > this.value){
if(this.right == null){
this.right = node;
}else{
this.right.add(node);
}
}
if(node.value < this.value){
if(this.left == null){
this.left = node;
}else{
this.left.add(node);
}
}
//添加完一个结点后,如果右子树的高度-左子树的高度大于1 ,左旋转
if(rightHeight() - leftHeight() > 1){
//如果它的左子树的右子树高度大于它的左子树高度时
if(right != null && right.leftHeight() > right.rightHeight()){
left.rightRotate();
leftRotate();
}else{
leftRotate();
}
return; //切记不可忘记
}
if(leftHeight() - rightHeight() > 1){
if(left != null && left.rightHeight() >left.leftHeight()){
left.leftRotate();
rightRotate();
}else{
rightRotate();
}
}
}
public void infixOrder(){
if(this.left != null){
this.left.infixOrder();
}
System.out.println(this);
if(this.right != null){
this.right.infixOrder();
}
}
}