• 树04自平衡二叉查找树AVL树


    一)AVL定义

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

    节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

    出自维基百科:http://zh.wikipedia.org/wiki/AVL%E6%A0%91

    二)AVL的实现(java)

    package com.fox.avl;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class AVLTree03<T extends Comparable<? super T>> {
    	private static class AvlNode<T> {
    		T e;
    		AvlNode<T> l;
    		AvlNode<T> r;
    		int h;
    
    		public AvlNode(T e, AvlNode<T> l, AvlNode<T> r) {
    			super();
    			this.e = e;
    			this.l = l;
    			this.r = r;
    			this.h = 0;
    		}
    
    		public AvlNode(T e) {
    			this(e, null, null);
    		}
    
    	}
    
    	AvlNode<T> root = null;
    
    	private int height(AvlNode<T> t) {
    		return (t == null) ? -1 : t.h;
    	}
    
    	public void insert(T x) {
    		root = insert(x, root);
    	}
    
    	private AvlNode<T> insert(T x, AvlNode<T> t) {
    		if (t == null)
    			return new AvlNode<T>(x);
    		int cr = x.compareTo(t.e);
    		if (cr < 0) {
    			t.l = insert(x, t.l);
    			if (height(t.l) - height(t.r) == 2)
    				if (x.compareTo(t.l.e) < 0)
    					t = rotateWithLeftChild(t);
    				else
    					t = doubleWithLeftChild(t);
    		} else if (cr > 0) {
    			t.r = insert(x, t.r);
    			if (height(t.r) - height(t.l) == 2)
    				if (x.compareTo(t.r.e) > 0)
    					t = rotateWithRightChild(t);
    				else
    					t = doubleWithRightChild(t);
    		} else
    			;
    		t.h = Math.max(height(t.l), height(t.r)) + 1;
    		return t;
    	}
    
    	// RL
    	private AvlNode<T> doubleWithRightChild(AvlNode<T> t) {
    		t.r = rotateWithLeftChild(t.r);
    		return rotateWithRightChild(t);
    	}
    
    	// RR
    	private AvlNode<T> rotateWithRightChild(AvlNode<T> t) {
    		AvlNode<T> t1 = t.r;
    		t.r = t1.l;
    		t1.l = t;
    		t.h = Math.max(height(t.l), height(t.r)) + 1;
    		t1.h = Math.max(height(t1.r), t.h) + 1;
    		return t1;
    	}
    
    	// LR
    	private AvlNode<T> doubleWithLeftChild(AvlNode<T> t) {
    		t.l = rotateWithRightChild(t.l);
    		return rotateWithLeftChild(t);
    	}
    
    	// LL
    	private AvlNode<T> rotateWithLeftChild(AvlNode<T> t) {
    		AvlNode<T> t1 = t.l;
    		t.l = t1.r;
    		t1.r = t;
    		t.h = Math.max(height(t.l), height(t.r)) + 1;
    		t1.h = Math.max(height(t1.l), t.h) + 1;
    		return t1;
    	}
    
    	public boolean contain(T x) {
    		return contain(x, root);
    	}
    
    	private boolean contain(T x, AvlNode<T> t) {
    		if (t == null)
    			return false;
    		int cr = x.compareTo(t.e);
    		if (cr < 0)
    			return contain(x, t.l);
    		else if (cr > 0)
    			return contain(x, t.r);
    		else
    			return true;
    	}
    
    	public void display() {
    		List<AvlNode<T>> ts = new ArrayList<AvlNode<T>>();
    		ts.add(root);
    		displayLevel(ts);
    	}
    
    	// 中序遍历
    	private void display(AvlNode<T> t) {
    		if (t == null)
    			return;
    		System.out.println(t.e + "[" + t.h + "]");
    		display(t.l);
    		display(t.r);
    	}
    
    	// 层次遍历
    	private void displayLevel(List<AvlNode<T>> t) {
    		if (t.size() == 0)
    			return;
    		List<AvlNode<T>> ts = new ArrayList<AvlNode<T>>();
    		for (AvlNode<T> t_ : t) {
    			if (t_ == null)
    				continue;
    			System.out.print(t_.e + "[" + t_.h + "]\t");
    			if (t_.l != null)
    				ts.add(t_.l);
    			if (t_.r != null)
    				ts.add(t_.r);
    		}
    		System.out.println();
    		displayLevel(ts);
    	}
    
    	public static void main(String[] f) {
    		AVLTree03<Integer> avl = new AVLTree03<Integer>();
    		avl.insert(1);
    		avl.insert(12);
    		avl.insert(3);
    		avl.insert(8);
    		avl.insert(2);
    		avl.insert(11);
    		avl.display();
    		System.out.println(avl.contain(1));
    		System.out.println(avl.contain(10));
    	}
    }
    

    这里注意插入节点的四种情况,下面一一细说。

    1)左左情况

    t1=t.l;

    step1:t.l = t1.r ;

    step2: t1.r = t ;

    经过“旋转”后如下图:

    2)右右情况

    t1 = t.r ;

    step1: t.r = t1.l ;

    step2: t1.l = t ;

    经过旋转后如下图:

    3)左右情况

    t1 = t.l ;

    t2 = t.r ;

    step1: t1.r = t2.l ;

    step2: t2.l = t1 ;

    以上步骤和右右情况一致,因此可以看成是对t1做右右操作,旋转后如下图:

    这个又和左左一模一样,因此在对t节点做左左操作。从代码中可以清楚的看出:

            // LR
    	private AvlNode<T> doubleWithLeftChild(AvlNode<T> t) {
    		t.l = rotateWithRightChild(t.l);//先对t1做右右操作
    		return rotateWithLeftChild(t);//在对t做左左操作
    	}
    

    4)右左情况

    和3)类似,不再复述。

    AVL的插入操作有点小复杂,但是删除操作比插入更复杂,如果偷懒那么还是选用标记删除吧!

  • 相关阅读:
    Ynoi 杂题选做
    CSP-S2021 浙江 游记
    P6272 [湖北省队互测2014]没有人的算术
    P5206 [WC2019]数树
    P5405 [CTS2019]氪金手游
    LOJ6679 Unknow
    LOJ3040 「JOISC 2019 Day4」合并
    P6122 [NEERC2016]Mole Tunnels
    三维凸包
    三,四元环计数
  • 原文地址:https://www.cnblogs.com/huangfox/p/2567454.html
Copyright © 2020-2023  润新知