算法系列(九)平衡二叉查找树AVL树

在 算法系列(八)数据结构之二叉查找树 中,介绍了二叉查找树的定义和基本操作。大部分操作的平均时间复杂度为O(logN),但是如果预先输入的数据有序,那么一连串的insert操作花费的时间就会很长,时间复杂度为O(N),一种解决方法是增加平衡附加条件,使得任何节点深度都不会过深。AVL树是最先发明的自平衡二叉查找树。

PS:写avl的时候突然发现之前的一个问题,将树的节点暴露了出去,这样不能保证平衡条件不被改变。github上的代码已经做了修改。外部不能直接访问TreeNode

概述:

在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis,他们在 1962 年的论文 “An algorithm for the organization of information” 中发表它。

定义和特性:

一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树。

高度为 h 的 AVL 树,节点数 N 最多2^h − 1; 最少N(h)=N(h− 1) +N(h− 2) + 1。

最少节点数

N(0) = 0 (表示 AVL Tree 高度为0的节点总数)
N(1) = 1 (表示 AVL Tree 高度为1的节点总数)
N(2) = 2 (表示 AVL Tree 高度为2的节点总数)

插入操作

简单插入一个节点,会破坏平衡二叉树的特性,恢复平衡性,需要做旋转操作。
插入操作之后,只有那些从插入点到根节点路径上的节点平衡关系会被改变,当我们沿着这条路径上行道根并更新平衡信息时,可以发现一个节点,它的新平衡破坏了AVL条件。我们把必须重新平衡的节点叫做α,对于任意节点,最多有两个儿子,出现高度不平衡时高度差2,这种不平衡出现在下面四种情况:
1对α的左儿子的左子树进行一次插入
2对α的左儿子的右子树进行一次插入

3对α的右儿子的左子树进行一次插入

4对α的右儿子的右子树进行一次插入

对于1,4可以使用单旋转解决,2,3可以使用双旋转解决。

单旋转

图解:
《算法系列(九)平衡二叉查找树AVL树》





分析:
抽象的形容:把树形象的看成柔软灵活的,抓住节点K1,使劲摇动它,使K1称为新的根。根据二叉查找树的性质,
K2>K1,K2>Y,于是K2变为K1的右子树,Y变为K2的左子树。
具体示例
《算法系列(九)平衡二叉查找树AVL树》

以上是对左儿子的操作,对右儿子的变化类似。

双旋转

图解:
《算法系列(九)平衡二叉查找树AVL树》

分析:
上图是左右双旋转,需要两次操作。
第一次对以k1为根节点的树进行单旋转。
第二次是以k3为根节点的树进行单旋转。
即,双旋转由两次单旋转构成

代码实现

package com.algorithm.tree;

import java.util.Comparator;

/**
 * AVL树
 * 
 * @author chao
 *
 * @param <T>
 */
public class AVLTree<T> extends BinarySeachTree<T> {

	public AVLTree(T root) {
		super(root);
	}

	public AVLTree(T root, Comparator<T> comparator) {
		super(root, comparator);
	}

	@Override
	protected BinaryTreeNode<T> insert(BinaryTreeNode<T> cur, BinaryTreeNode<T> x) {
		if (cur == null) {
			return x;
		}
		int compareresult = compare(cur, x);
		if (compareresult < 0) {
			cur.right = insert(cur.right, x);
			if (getHeight(cur.right) - getHeight(cur.left) == 2) {
				if (compare(x, cur.right) > 0) {
					cur = roateWithRightChild(cur);
				} else {
					cur = doubleWithRightChild(cur);
				}
			}
		} else if (compareresult > 0) {
			cur.left = insert(cur.left, x);
			if (getHeight(cur.left) - getHeight(cur.right) == 2) {
				if (compare(x, cur.left) < 0) {
					cur = roateWithLeftChild(cur);
				} else {
					cur = doubleWithLeftChild(cur);
				}
			}
		} else {
			cur.hintcount++;
		}
		return cur;
	}

	/**
	 * 左子树单旋转
	 * 
	 * @param node
	 * @return
	 */
	protected BinaryTreeNode<T> roateWithLeftChild(BinaryTreeNode<T> k2) {
		BinaryTreeNode<T> k1 = k2.left;
		k2.left = k1.right;
		k1.right = k2;
		return k1;

	}

	/**
	 * 右子树单旋转
	 * 
	 * @param node
	 * @return
	 */
	protected BinaryTreeNode<T> roateWithRightChild(BinaryTreeNode<T> k2) {
		BinaryTreeNode<T> k1 = k2.right;
		k2.right = k1.left;
		k1.left = k2;
		return k1;
	}

	/**
	 * 左子树双旋转
	 * 
	 * @param node
	 * @return
	 */
	protected BinaryTreeNode<T> doubleWithLeftChild(BinaryTreeNode<T> k3) {
		k3.left = roateWithRightChild(k3.left);
		return roateWithLeftChild(k3);

	}

	/**
	 * 右子树双旋转
	 * 
	 * @param node
	 * @return
	 */
	protected BinaryTreeNode<T> doubleWithRightChild(BinaryTreeNode<T> k3) {
		k3.right = roateWithLeftChild(k3.right);
		return roateWithRightChild(k3);
	}
}

删除操作也会破坏平衡,这里直接使用懒删除

@Override
	public void remove(T x) {
		BinaryTreeNode<T> node = findNode(root, x);
		if (node == null || node.hintcount < 0) {
			return;
		} else {
			node.hintcount--;
		}
	}








代码实现可以看github,地址https://github.com/robertjc/simplealgorithm
github代码也在不断完善中,有些地方可能有问题,还请多指教




欢迎扫描二维码,关注公众账号

《算法系列(九)平衡二叉查找树AVL树》

    原文作者:AVL树
    原文地址: https://blog.csdn.net/robertcpp/article/details/51628172
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞