数据结构数之平衡二叉树(AVL)

这里主要解决下面问题
(1)什么是平衡二叉树
(2)为什么需要平衡二叉树
(3)实现平衡二叉树(添加和删除)

1、什么是平衡二叉树
(1)是二叉树
(2)每一个节点的左右子树的高度差小于等于1
比如下面的树
在这里插入图片描述
这个树看似不怎么平衡,但是这个就是平衡二叉树。上面的每个节点左边的数字表示节点的高度(叶子节点高度为1),右边的数字表示节点的平衡因子(左子节点高度-有子节点高度,叶子节点平衡因子为0)

2、为什么需要平衡二叉树
我们都知道,如果插入的序列已经是排好序的,那么对于二叉树的查找的效率就退化为对链表的查找(O(logn))。序列的总数比较多的时候,查找的效率将会发生很大的退化。下面我们做一效率的比较
我按顺序把1-10000的数字插入平衡二叉树和普通二叉树中,然后再从查找1-10000,看看运行的时间
测试的代码如下:

	public static void main(String[] args) {
		BST<Integer, Object> bst=new BST<>();
		AVLTree<Integer, Object> avl=new AVLTree<>();
		//BST
		long startTime = System.nanoTime();
		for(int i=1;i<=10000;i++)
		{
			bst.add(i);
		}
		for(int i=1;i<=10000;i++)
		{
			bst.get(i);
		}
		long endTime = System.nanoTime();
		System.out.println("BST: "+(endTime-startTime)/(100000000-0.0)+"s");
		
		//AVL
		startTime = System.nanoTime();
		for(int i=1;i<=10000;i++)
		{
			avl.add(i);
		}
		for(int i=1;i<=10000;i++)
		{
			avl.get(i);
		}
		endTime = System.nanoTime();
		System.out.println("AVL: "+(endTime-startTime)/(100000000-0.0)+"s");
	}

结果:
在这里插入图片描述
上面可以得知:平衡二叉树的效率比普通二叉树有了很大的提高

3、实现平衡二叉树
(1)需要考虑的问题
为了在添加和删除节点的时候知道这个节点是否是平衡的,所以我们要在树的节点添加一个数据项来记录节点的高度,然后通过其左子节点和右子节点的高度差得到平衡因子,最后根据平衡因子来判断这个节点是否是平衡的。如果不平衡,那么进行平衡

	private class Node<K,V>{
		//为了具有拓展性,比如AVL树可以用来实现map和set(set就把value设为null)
		private K key;
		private V value;
		
		//左孩子
		private Node<K,V> left;
		private Node<K,V> right;
		
		//这个节点的高度
		private int height;
		
		
		//构造函数
		public Node(K key)
		{
			this(key, null);
		}
		public Node(K key,V value)
		{
			this.key=key;
			this.value=value;
			//初始高度为1
			height=1;
		}
		
	}

这个是一个私有的内部类
(2)添加节点
AVL树的添加主要考虑下面4种情况

–LL:在这个节点的左子节点的左子节点位置插入,右旋转

在这里插入图片描述
– RR:在这个节点的右子节点的右子节点位置插入,左旋转
在这里插入图片描述
– LR:在这个节点的左子节点的右子节点位置插入,左旋转后右旋转
在这里插入图片描述
– RL:在这个节点的右子节点的左子节点位置插入,右旋转后左旋转
和上面的LR对称,这里就不画了
递归实现的代码

	public void add(K key)
	{
		add(key, null);
	}
	
	public void add(K key, V value)
	{
		//使用递归来实现
		root=add(root, key, value);
	}
	
	private Node<K, V> add(Node<K, V> node, K key, V value)
	{
		//如果这个节点为空,那么新建一个节点返回给它的父节点
		if(node==null)
		{
			size++;
			return new Node<K, V>(key, value);
		}
		//如果key小于目前节点,进入其左子树
		if(key.compareTo(node.key)<0)
			node.left=add(node.left, key, value);
		//如果key大于目前节点,进入其右子树
		else if(key.compareTo(node.key)>0)
			node.right=add(node.right, key, value);
		else
			node.value=value;
		
		//更新节点过度
		node.height=Math.max(getHeight(node.left), getHeight(node.right))+1;
		
		//这个节点的平衡因子
		int balanceFactor=getHeight(node.left)-getHeight(node.right);
		//System.out.println("balanceFactor="+balanceFactor+"  "+node.key);
		//如果平衡因子大于1或者小于-1,则需要平衡
		//LL
		if(balanceFactor>1&&getBalanceFactor(node.left)>0)
		{
			return rightRotate(node);
		}
		//RR
		if(balanceFactor<-1&&getBalanceFactor(node.right)<0)
		{
			//System.out.println("RR"+"  "+node.key+"  "+getBalanceFactor(node));
			return leftRotate(node);
		}
		//LR
		if(balanceFactor>1&&getBalanceFactor(node.left)<0)
		{
			//先左旋转在右旋转
			node.left=leftRotate(node.left);
			return rightRotate(node);
		}
		//RL
		if(balanceFactor<-1&&getBalanceFactor(node.right)>0)
		{
			//先右旋转在左旋转
			node.right=rightRotate(node.right);
			return leftRotate(node);
		}
		
		return node;
	}
	
	//右旋转
	private Node<K, V> rightRotate(Node<K, V> node)
	{
		Node<K, V> node1=node.left;
		Node<K, V> node2=node1.right;
		
		node1.right=node;
		node.left=node2;
		node.height=Math.max(getHeight(node.left), getHeight(node.right))+1;
		node1.height=Math.max(getHeight(node1.left), getHeight(node1.right))+1;
		return node1;
	}
	
	//左旋转
	private Node<K, V> leftRotate(Node<K, V> node)
	{
		Node<K, V> node1=node.right;
		Node<K, V> node2=node1.left;
		
		node1.left=node;
		node.right=node2;
		node.height=Math.max(getHeight(node.left), getHeight(node.right))+1;
		node1.height=Math.max(getHeight(node1.left), getHeight(node1.right))+1;
		return node1;
	}
	

(3)删除节点,删除节点的平衡和添加节点的完全一样,当删除节点递归返回后,判断节点是否平衡,如果不平衡,那么需要先进行平衡,最后把平衡后这个子树的根节点返回
代码实现

	public void remove(K key)
	{
		root=remove(root,key);
	}
	private Node<K, V> remove(Node<K, V> node,K key)
	{
		//如果node为空,表示找不到
		if(node==null)
			return null;
		
		Node<K, V> retNode;
		
		//key小于node的key,那么在其左子树上删除
		if(key.compareTo(node.key)<0)
		{
			node.left=remove(node.left,key);
			retNode=node;
		}
		//key大于node的key,那么在其右子树上删除
		else if(key.compareTo(node.key)>0)
		{
			node.right=remove(node.right, key);
			retNode=node;
		}
		//key等于node的key,那么删除node,然后维护这个新树
		else
		{
			size--;
			//表示删除node
			//node左子树如果是null,那么返回其右子树即可
			if(node.left==null)
				retNode=node.right;
			//node左子树不为null,右子树是null,那么返回其左子树即可
			else if(node.right==null)
				retNode=node.left;
			//node的左右子树都不为null,那么返回node节点中序遍历的后继节点,同时维护这棵新树
			else {
				//node的右子节点
				Node<K, V> nodeMidNext=node.right;
				//如果nodeRight的左子树为空,那nodeRight就是node节点中序遍历的后继节点
				if(nodeMidNext.left==null)
				{
					//维护新数
					nodeMidNext.left=node.left;
					retNode=nodeMidNext;
				}
				//如果其左子树不为null,那么nodeRight的最左子树就是node节点中序遍历的后继节点
				else
				{
					//这个后继节点的父节点
					Node<K, V> nodeMidNextParent=nodeMidNext;
					nodeMidNext=nodeMidNext.left;
					//找到这个后继节点
					while(nodeMidNext.left!=null)
					{
						nodeMidNextParent=nodeMidNext;
						nodeMidNext=nodeMidNext.left;
					}
					//维护新数
					nodeMidNextParent.left=nodeMidNext.right;
					nodeMidNext.left=node.left;
					nodeMidNext.right=node.right;
					retNode=nodeMidNext;
				}
			}
		}
		
		if(retNode!=null)
		{
			//维护平衡
			int balanceFactor=getHeight(retNode.left)-getHeight(retNode.right);
			System.out.println("balanceFactor="+balanceFactor+"  "+retNode.key);
			//如果平衡因子大于1或者小于-1,则需要平衡
			//LL
			if(balanceFactor>1&&getBalanceFactor(retNode.left)>0)
			{
				return rightRotate(retNode);
			}
			//RR
			if(balanceFactor<-1&&getBalanceFactor(retNode.right)<0)
			{
				return leftRotate(retNode);
			}
			//LR
			if(balanceFactor>1&&getBalanceFactor(retNode.left)<0)
			{
				//先左旋转在右旋转
				retNode.left=leftRotate(retNode.left);
				return rightRotate(retNode);
			}
			//RL
			if(balanceFactor<-1&&getBalanceFactor(retNode.right)>0)
			{
				//先右旋转在左旋转
				retNode.right=rightRotate(retNode.right);
				return leftRotate(retNode);
			}
		}
		
		return retNode;
		
	}
	public int size()
	{
		return this.size;
	}
	
	private int getHeight(Node<K, V> node)
	{
		if(node==null)
			return 0;
		return node.height;
	}
	
	private int getBalanceFactor(Node<K, V> node)
	{
		if(node==null)
			return 0;
		return (getHeight(node.left)-getHeight(node.right));
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值