zoukankan      html  css  js  c++  java
  • AVL(平衡二叉搜索树)

    简介

    AVL树,是一种平衡(balanced)的二叉搜索树(binary search tree, 简称为BST)。由两位科学家在1962年发表的论文《An algorithm for the organization of information》当中提出,作者是发明者G.M. Adelson-VelskyE.M. Landis(链接由维基百科提供)。它具有以下两个性质:

    • 任意一个结点的key,比它的左孩子key大,比它的右孩子key小;
    • 任意结点的孩子结点之间高度差距最大为1;

    那 AVL 树和普通的二叉查找树有何区别呢?如图,如果我们插入的是一组有序上升或下降的数据,则一棵普通的二叉查找树必然会退化成一个单链表,其查找效率就降为 (O(n))。而 AVL 树因其平衡的限制,可以始终保持 (O(log~n)) 的时间复杂度。

    树的旋转(左旋,右旋)

    img

    上面动图直观的感受就是右旋后右子树高度升高,左子树高度降低;左旋后左子树升高,右子树高度降低;除此之外,旋转的过程中也涉及到节点的交换

    从上图可以看到,当简单地说右旋,其实展开来说是指:

    • 根节点 5 右旋,首先将左子树 3 的右孩子 4 作为此时根节点 5 的左孩子;
    • 再将 5 这棵树作为新根节点 3 的右子树;

    左旋反之;因为这样很啰嗦,平时不会这么说,但这背后的原理得知道。此外旋转后节点还是符合大小排列顺序,这正是我们所希望的。

    4 种失衡

    上面说到可能导致失衡的隐患,分别是右重和左重。你可能在很多地方看到 LL(左左)、RR(右右)、LR(左右)、RL(右左),搞得跟秘籍键似的这 TM 到底指的是啥?其实就是下面的 4 种失衡情况:

    以下统一约定:红色结点为新插入结点,y 结点为失衡结点

    1. 左左失衡

    img

    所谓的左左,即 "失衡结点" 的左子树比右子树高 2,左孩子(即 x)下的左子树比右子树高 1。

    我们只需对 "以 y 为根的子树" 进行 "左左旋转 (ll_rotate)" 即可。一次旋转后,恢复平衡。

    2. 右右失衡

    img

    所谓的右右,即 "失衡结点" 的右子树比左子树高 2,右孩子(即 x)下的右子树比左子树高 1。

    我们只需对 "以 y 为根的子树" 进行 "右右旋转 (rr_rotate)" 即可。一次旋转后,恢复平衡。

    3. 左右失衡

    img

    所谓的左右,即 "失衡结点" 的左子树比右子树高 2,左孩子(即 x)下的右子树比左子树高 1。

    观察发现,若先对 "以 x 为根的子树" 进行 "右右旋转 (rr_rotate)",此时 "以 y 为根的子树" 恰好符合 "左左失衡",所以再进行一次 "左左旋转 (ll_rotate)"。两次旋转后,恢复平衡。

    4. 右左失衡

    img

    所谓的右左,即 "失衡结点" 的右子树比左子树高 2,右孩子(即 x)下的左子树比右子树高 1。

    观察发现,若先对 "以 x 为根的子树" 进行 "左左旋转 (ll_rotate)",此时 "以 y 为根的子树" 恰好符合 "右右失衡",所以再进行一次 "右右旋转 (rr_rotate)"。两次旋转后,恢复平衡。

    AVL树重新平衡

    代码实现:

    public class AVLTree<T extends Comparable<T>> {
    	private AVLTreeNode<T> mRoot;    // 根结点
    
    	// AVL树的节点(内部类)
    	class AVLTreeNode<T extends Comparable<T>> {
    		T key;                // 关键字(键值)
    		int height;         // 高度
    		AVLTreeNode<T> left;    // 左孩子
    		AVLTreeNode<T> right;    // 右孩子
    
    		public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
    			this.key = key;
    			this.left = left;
    			this.right = right;
    			this.height = 0;
    		}
    	}
    
    	// 构造函数
    	public AVLTree() {
    		mRoot = null;
    	}
    
    	/*
    	 * 获取树的高度
    	 */
    	private int height(AVLTreeNode<T> tree) {
    		if (tree != null)
    			return tree.height;
    
    		return 0;
    	}
    
    	public int height() {
    		return height(mRoot);
    	}
    
    	/*
    	 * 比较两个值的大小
    	 */
    	private int max(int a, int b) {
    		return a>b ? a : b;
    	}
    
    	/*
    	 * 前序遍历"AVL树"
    	 */
    	private void preOrder(AVLTreeNode<T> tree) {
    		if(tree != null) {
    			System.out.print(tree.key+" ");
    			preOrder(tree.left);
    			preOrder(tree.right);
    		}
    	}
    
    	public void preOrder() {
    		preOrder(mRoot);
    	}
    
    	/*
    	 * 中序遍历"AVL树"
    	 */
    	private void inOrder(AVLTreeNode<T> tree) {
    		if(tree != null)
    		{
    			inOrder(tree.left);
    			System.out.print(tree.key+" ");
    			inOrder(tree.right);
    		}
    	}
    
    	public void inOrder() {
    		inOrder(mRoot);
    	}
    
    	/*
    	 * 后序遍历"AVL树"
    	 */
    	private void postOrder(AVLTreeNode<T> tree) {
    		if(tree != null) {
    			postOrder(tree.left);
    			postOrder(tree.right);
    			System.out.print(tree.key+" ");
    		}
    	}
    
    	public void postOrder() {
    		postOrder(mRoot);
    	}
    
    	/*
    	 * (递归实现)查找"AVL树x"中键值为key的节点
    	 */
    	private AVLTreeNode<T> search(AVLTreeNode<T> x, T key) {
    		if (x==null)
    			return x;
    
    		int cmp = key.compareTo(x.key);
    		if (cmp < 0)
    			return search(x.left, key);
    		else if (cmp > 0)
    			return search(x.right, key);
    		else
    			return x;
    	}
    
    	public AVLTreeNode<T> search(T key) {
    		return search(mRoot, key);
    	}
    
    	/*
    	 * (非递归实现)查找"AVL树x"中键值为key的节点
    	 */
    	private AVLTreeNode<T> iterativeSearch(AVLTreeNode<T> x, T key) {
    		while (x!=null) {
    			int cmp = key.compareTo(x.key);
    
    			if (cmp < 0)
    				x = x.left;
    			else if (cmp > 0)
    				x = x.right;
    			else
    				return x;
    		}
    
    		return x;
    	}
    
    	public AVLTreeNode<T> iterativeSearch(T key) {
    		return iterativeSearch(mRoot, key);
    	}
    
    	/*
    	 * 查找最小结点:返回tree为根结点的AVL树的最小结点。
    	 */
    	private AVLTreeNode<T> minimum(AVLTreeNode<T> tree) {
    		if (tree == null)
    			return null;
    
    		while(tree.left != null)
    			tree = tree.left;
    		return tree;
    	}
    
    	public T minimum() {
    		AVLTreeNode<T> p = minimum(mRoot);
    		if (p != null)
    			return p.key;
    
    		return null;
    	}
    
    	/*
    	 * 查找最大结点:返回tree为根结点的AVL树的最大结点。
    	 */
    	private AVLTreeNode<T> maximum(AVLTreeNode<T> tree) {
    		if (tree == null)
    			return null;
    
    		while(tree.right != null)
    			tree = tree.right;
    		return tree;
    	}
    
    	public T maximum() {
    		AVLTreeNode<T> p = maximum(mRoot);
    		if (p != null)
    			return p.key;
    
    		return null;
    	}
    
    	/*
    	 * LL:左左对应的情况(左单旋转)。
    	 *
    	 * 返回值:旋转后的根节点
    	 */
    	private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> k2) {
    		AVLTreeNode<T> k1;
    
    		k1 = k2.left;
    		k2.left = k1.right;
    		k1.right = k2;
    
    		k2.height = max( height(k2.left), height(k2.right)) + 1;
    		k1.height = max( height(k1.left), k2.height) + 1;
    
    		return k1;
    	}
    
    	/*
    	 * RR:右右对应的情况(右单旋转)。
    	 *
    	 * 返回值:旋转后的根节点
    	 */
    	private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> k1) {
    		AVLTreeNode<T> k2;
    
    		k2 = k1.right;
    		k1.right = k2.left;
    		k2.left = k1;
    
    		k1.height = max( height(k1.left), height(k1.right)) + 1;
    		k2.height = max( height(k2.right), k1.height) + 1;
    
    		return k2;
    	}
    
    	/*
    	 * LR:左右对应的情况(左双旋转)。
    	 *
    	 * 返回值:旋转后的根节点
    	 */
    	private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> k3) {
    		k3.left = rightRightRotation(k3.left);
    
    		return leftLeftRotation(k3);
    	}
    
    	/*
    	 * RL:右左对应的情况(右双旋转)。
    	 *
    	 * 返回值:旋转后的根节点
    	 */
    	private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> k1) {
    		k1.right = leftLeftRotation(k1.right);
    
    		return rightRightRotation(k1);
    	}
    
    	/*
    	 * 将结点插入到AVL树中,并返回根节点
    	 *
    	 * 参数说明:
    	 *     tree AVL树的根结点
    	 *     key 插入的结点的键值
    	 * 返回值:
    	 *     根节点
    	 */
    	private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {
    		if (tree == null) {
    			// 新建节点
    			tree = new AVLTreeNode<T>(key, null, null);
    			if (tree==null) {
    				System.out.println("ERROR: create avltree node failed!");
    				return null;
    			}
    		} else {
    			int cmp = key.compareTo(tree.key);
    
    			if (cmp < 0) {    // 应该将key插入到"tree的左子树"的情况
    				tree.left = insert(tree.left, key);
    				// 插入节点后,若AVL树失去平衡,则进行相应的调节。
    				if (height(tree.left) - height(tree.right) == 2) {
    					if (key.compareTo(tree.left.key) < 0)
    						tree = leftLeftRotation(tree);
    					else
    						tree = leftRightRotation(tree);
    				}
    			} else if (cmp > 0) {    // 应该将key插入到"tree的右子树"的情况
    				tree.right = insert(tree.right, key);
    				// 插入节点后,若AVL树失去平衡,则进行相应的调节。
    				if (height(tree.right) - height(tree.left) == 2) {
    					if (key.compareTo(tree.right.key) > 0)
    						tree = rightRightRotation(tree);
    					else
    						tree = rightLeftRotation(tree);
    				}
    			} else {    // cmp==0
    				System.out.println("添加失败:不允许添加相同的节点!");
    			}
    		}
    
    		tree.height = max( height(tree.left), height(tree.right)) + 1;
    
    		return tree;
    	}
    
    	public void insert(T key) {
    		mRoot = insert(mRoot, key);
    	}
    
    	/*
    	 * 删除结点(z),返回根节点
    	 *
    	 * 参数说明:
    	 *     tree AVL树的根结点
    	 *     z 待删除的结点
    	 * 返回值:
    	 *     根节点
    	 */
    	private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> z) {
    		// 根为空 或者 没有要删除的节点,直接返回null。
    		if (tree==null || z==null)
    			return null;
    
    		int cmp = z.key.compareTo(tree.key);
    		if (cmp < 0) {        // 待删除的节点在"tree的左子树"中
    			tree.left = remove(tree.left, z);
    			// 删除节点后,若AVL树失去平衡,则进行相应的调节。
    			if (height(tree.right) - height(tree.left) == 2) {
    				AVLTreeNode<T> r =  tree.right;
    				if (height(r.left) > height(r.right))
    					tree = rightLeftRotation(tree);
    				else
    					tree = rightRightRotation(tree);
    			}
    		} else if (cmp > 0) {    // 待删除的节点在"tree的右子树"中
    			tree.right = remove(tree.right, z);
    			// 删除节点后,若AVL树失去平衡,则进行相应的调节。
    			if (height(tree.left) - height(tree.right) == 2) {
    				AVLTreeNode<T> l =  tree.left;
    				if (height(l.right) > height(l.left))
    					tree = leftRightRotation(tree);
    				else
    					tree = leftLeftRotation(tree);
    			}
    		} else {    // tree是对应要删除的节点。
    			// tree的左右孩子都非空
    			if ((tree.left!=null) && (tree.right!=null)) {
    				if (height(tree.left) > height(tree.right)) {
    					// 如果tree的左子树比右子树高;
    					// 则(01)找出tree的左子树中的最大节点
    					//   (02)将该最大节点的值赋值给tree。
    					//   (03)删除该最大节点。
    					// 这类似于用"tree的左子树中最大节点"做"tree"的替身;
    					// 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的。
    					AVLTreeNode<T> max = maximum(tree.left);
    					tree.key = max.key;
    					tree.left = remove(tree.left, max);
    				} else {
    					// 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)
    					// 则(01)找出tree的右子树中的最小节点
    					//   (02)将该最小节点的值赋值给tree。
    					//   (03)删除该最小节点。
    					// 这类似于用"tree的右子树中最小节点"做"tree"的替身;
    					// 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的。
    					AVLTreeNode<T> min = maximum(tree.right);
    					tree.key = min.key;
    					tree.right = remove(tree.right, min);
    				}
    			} else {
    				AVLTreeNode<T> tmp = tree;
    				tree = (tree.left!=null) ? tree.left : tree.right;
    				tmp = null;
    			}
    		}
    
    		return tree;
    	}
    
    	public void remove(T key) {
    		AVLTreeNode<T> z;
    
    		if ((z = search(mRoot, key)) != null)
    			mRoot = remove(mRoot, z);
    	}
    
    	/*
    	 * 销毁AVL树
    	 */
    	private void destroy(AVLTreeNode<T> tree) {
    		if (tree==null)
    			return ;
    
    		if (tree.left != null)
    			destroy(tree.left);
    		if (tree.right != null)
    			destroy(tree.right);
    
    		tree = null;
    	}
    
    	public void destroy() {
    		destroy(mRoot);
    	}
    
    	/*
    	 * 打印"二叉查找树"
    	 *
    	 * key        -- 节点的键值
    	 * direction  --  0,表示该节点是根节点;
    	 *               -1,表示该节点是它的父结点的左孩子;
    	 *                1,表示该节点是它的父结点的右孩子。
    	 */
    	private void print(AVLTreeNode<T> tree, T key, int direction) {
    		if(tree != null) {
    			if(direction==0)    // tree是根节点
    				System.out.printf("%2d is root
    ", tree.key, key);
    			else                // tree是分支节点
    				System.out.printf("%2d is %2d's %6s child
    ", tree.key, key, direction==1?"right" : "left");
    
    			print(tree.left, tree.key, -1);
    			print(tree.right,tree.key,  1);
    		}
    	}
    
    	public void print() {
    		if (mRoot != null)
    			print(mRoot, mRoot.key, 0);
    	}
    }
    

    参考:

    AVL 树

    AVL树(三)之 Java的实现

    彻底搞懂AVL树

    AVL树的旋转图解和简单实现

    看图轻松理解数据结构与算法系列(AVL树)

    数据结构与算法(5):AVL树

    AVL 树

    AVLTreeST.java

    数据结构之平衡二叉树(AVL)---Java实现

    AVL Tree

    平衡二叉树之AVL树(Adelson-Velsky and Landis Tree)简介及Java实现

    数据结构之平衡二叉树(AVL)---Java实现

  • 相关阅读:
    Flask基础
    Scrapy框架(持久化,去重,深度控制,cookie)
    scrapy框架 简易整理
    BeautifulSoup 模块
    requests模块
    复习第三天
    在Django中使用原生Sql
    ajax跨域简单请求和复杂请求
    复习第二天
    IOS
  • 原文地址:https://www.cnblogs.com/hongdada/p/12166636.html
Copyright © 2011-2022 走看看