数据结构之自平衡二叉查找树(1)

今天开始,我们再来认识一个新的二叉树,称为自平衡二叉查找树。AVL树是最先发明的自平衡二叉查找树。

AVL树的特点是:对于树中的任何节点,节点的左右子树的高度差距最大为1,所以AVL树也称为高度平衡树。AVL树得名于它的发明者G.M. Adelson-VelskyE.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。

我们定义节点的平衡因子就是该节点的左子树高度与右子树高度之差,根据上面的描述我们可以知道,带有+1,0,-1平衡因子的节点是平衡节点,其他的都为非平衡节点。当节点是非平衡的时候,我们需要对该节点进行平衡化。我们既可以将平衡因子保存在节点的内部,也可以通过节点的高度计算出来。

高度为h的AVL树,节点数N最多2^h -1; 最少\frac { \Phi ^{h+2}} {\sqrt{5}} -1 ( 其中\Phi = \frac {1 + \sqrt{5} } {2} = 1.618 )。
最少节点数n如以费伯纳西数列可以用数学归纳法证明:
N_h =F_{h+2} - 1 (F_{h+2}是Fibonacci polynomial)。
即:
N_0 = 0 (表示AVL Tree高度为0的节点总数)
N_1 = 1 (表示AVL Tree高度为1的节点总数)
N_2 = 2 (表示AVL Tree高度为2的节点总数)
N_h =N_{h-1} +N_{h-2} + 1 (表示AVL Tree高度为h的节点总数)
换句话说,当节点数为N时,高度h最多为log_{\Phi} ( \sqrt{5} +(N+1)) -2

 

对于非平衡节点的平衡化一般有四种情况,现在列在下面:

这四种方式的代码实现如下:

	//单旋左子节点
	private AVLTreeNode rotateWithLeftChild(AVLTreeNode node)
	{
		AVLTreeNode root=node.left;
		node.left=root.right;
		root.right=node;
		node.height=Math.max(height(node.left),height(node.right))+1;
		root.height=Math.max(height(root.left),height(root.right))+1;
		return root;
	}
	
	//单旋右子节点
	private AVLTreeNode rotateWithRightChild(AVLTreeNode node)
	{
		AVLTreeNode root=node.right;
		node.right=root.left;
		root.left=node;
		node.height=Math.max(height(node.left),height(node.right))+1;
		root.height=Math.max(height(root.left),height(root.right))+1;
		return root;
	}
	
	//双旋左子节点
	private AVLTreeNode doubleRotateLeftChild(AVLTreeNode node)
	{
		node.left=rotateWithRightChild(node.left);
		return rotateWithLeftChild(node);
	}
	
	//双旋右子节点
	private AVLTreeNode doubleRotateRightChild(AVLTreeNode node)
	{
		node.left=rotateWithLeftChild(node.right);
		return rotateWithRightChild(node);
	}

 

AVL树的操作包含插入,删除和查找三个基本操作,这三种情况在平均和最坏情况下的时间复杂度都为O(log n)。

插入:

向AVL树插入可以通过如同它是未平衡的二叉查找树一样把给定的值插入树中,接着自底向上向根节点折回,于在插入期间成为不平衡的所有节点上进行旋转来完成。因为折回到根节点的路途上最多有1.5乘logn个节点,而每次AVL旋转都耗费恒定的时间,插入处理在整体上耗费O(logn) 时间。

删除:

从AVL树中删除可以通过把要删除的节点向下旋转成一个叶子节点,接着直接剪除这个叶子节点来完成。因为在旋转成叶子节点期间最多有log n个节点被旋转,而每次AVL旋转耗费恒定的时间,删除处理在整体上耗费O(logn) 时间。

查找:

可以像普通二叉查找树一样的进行,所以耗费O(log n)时间,因为AVL树总是保持平衡的。不需要特殊的准备,树的结构不会由于查找而改变。(这是与伸展树查找相对立的,它会因为查找而变更树结构。)

为了便于观察自平衡二叉查找树和一般的二叉查找树的区别,我们下面将对同一个二叉树给出遍历的两种不同结果,通过遍历的结果我们可以知道二叉树的结构情况。下面首先给出一般二叉查找树的程序:

package mianshiti;
import java.util.*;

public class BinarySearchTree {
	private TreeNode root=null;//根节点
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		BinarySearchTree mytree=new BinarySearchTree();
		mytree.insert(4);
		mytree.insert(3);
		mytree.insert(2);
		mytree.insert(1);
		mytree.insert(5);
		System.out.println("递归方式实现前序遍历:");
		mytree.preorder(mytree.getRoot());
		System.out.println();
		System.out.println("递归方式实现中序遍历:");
		mytree.inorder(mytree.getRoot());
		System.out.println();
		System.out.println("递归方式实现后序遍历:");
		mytree.postorder(mytree.getRoot());
		System.out.println();
		System.out.println("非递归方式实现前序遍历:");
		mytree.nonRecursivePreorder(mytree.getRoot());
		System.out.println();
		System.out.println("非递归方式实现中序遍历:");
		mytree.nonRecursiveInorder(mytree.getRoot());
		System.out.println();
		System.out.println("非递归方式实现后序遍历:");
		mytree.nonRecursivePostorder(mytree.getRoot());
		System.out.println();
	}
	
	//前序遍历
	public void preorder(TreeNode node)
	{
		if(node!=null)
		{
			this.getNodeInfo(node);
			preorder(node.left);
			preorder(node.right);
		}
	}
	//中序遍历
	public void inorder(TreeNode node)
	{
		if(node!=null)
		{
			inorder(node.left);
			this.getNodeInfo(node);
			inorder(node.right);
		}
	}
	//后序遍历
	public void postorder(TreeNode node)
	{
		if(node!=null)
		{
			postorder(node.left);
			postorder(node.right);
			this.getNodeInfo(node);
		}
	}
	//非递归方式实现前序遍历
	public void nonRecursivePreorder(TreeNode node)
	{
		Stack<TreeNode> mystack=new Stack<TreeNode>();
		if(node!=null)
		{
			mystack.push(node);
			while(!mystack.isEmpty())
			{
				TreeNode temp=mystack.pop();
				this.getNodeInfo(temp);
				if(temp.right!=null)
				{
					mystack.push(temp.right);
				}
				if(temp.left!=null)
				{
					mystack.push(temp.left);
				}
			}
		}
	}
	//非递归方式实现中序遍历
	public void nonRecursiveInorder(TreeNode node)
	{
		Stack<TreeNode> mystack=new Stack<TreeNode>();
		while(node!=null)
		{
			while(node!=null)
			{
				if(node.right!=null)
				{
					mystack.push(node.right);
				}
				mystack.push(node);
				node=node.left;
			}
			node=mystack.pop();
			while(!mystack.isEmpty()&&node.right==null)
			{
				this.getNodeInfo(node);
				node=mystack.pop();
			}
			this.getNodeInfo(node);
			if(!mystack.isEmpty())
			{
				node=mystack.pop();
			}else
			{
				node=null;
			}
		}
	}
	//非递归方式实现后续遍历
	public void nonRecursivePostorder(TreeNode node)
	{
		Stack<TreeNode> mystack=new Stack<TreeNode>();
		TreeNode temp=node;
		while(node!=null)
		{
			for(;node.left!=null;node=node.left)
			{
				mystack.push(node);
			}
			while(node!=null&&(node.right==null||node.right==temp))
			{
				this.getNodeInfo(node);
				temp=node;
				if(mystack.isEmpty())
					return;
				node=mystack.pop();
			}
			mystack.push(node);
			node=node.right;
		}
	}

	//构造函数
	public BinarySearchTree()
	{
		this.root=null;
	}
	
	
	public TreeNode getRoot()
	{
		return this.root;
	}
	
	//添加一个节点
	public void insert(int value)
	{
		TreeNode nextnode=new TreeNode(value);
		TreeNode temp=this.root;
		if(this.root==null)
		{
			this.root=nextnode;
			nextnode.parent=null;
			return ;
		}
		while(temp!=null)
		{
			if(value<temp.data)
			{
				if(temp.left==null)
				{
					temp.left=nextnode;
					nextnode.parent=temp;
					return;
				}else
				{
					temp=temp.left;
				}
			}else if(value>temp.data)
			{
				if(temp.right==null)
				{
					temp.right=nextnode;
					nextnode.parent=temp;
					return;
				}else
				{
					temp=temp.right;
				}
			}
		}
	}
	
	//获取以begin为根节点的树的最小关键字节点的引用
	private TreeNode minTreeNode(TreeNode begin)
	{
		if(begin==null)
			return null;
		while(begin.left!=null)
		{
			begin=begin.left;
		}
		return begin;
	}
	
	//获取以begin为根节点的树的最大关键字节点的引用
	private TreeNode maxTreeNode(TreeNode begin)
	{
		if(begin==null)
			return null;
		while(begin.right!=null)
		{
			begin=begin.right;
		}
		return begin;
	}
	
	
	//删除一个节点
	public void delete(int value)
	{
		if(this.root==null)
		{
			System.out.println("树为空,无法删除"+value);
			return;
		}
		TreeNode temp=this.root;
		while(temp!=null)
		{
			if(value>temp.data)
			{
				if(temp.right==null)
				{
					System.out.println("树中没有要删除的项");
				}else
				{
					temp=temp.right;
				}
			}else if(value<temp.data)
			{
				if(temp.left==null)
				{
					System.out.println("树中没有要删除的项");
				}else
				{
					temp=temp.left;
				}
			}else
			{
				//找到了这个节点
				if(temp.left==null&&temp.right==null)//该节点是叶节点
				{
					if(temp.parent.right==temp)
					{
						temp.parent.right=null;
					}else
					{
						temp.parent.left=null;
					}
				}else if(temp.right!=null&&temp.left==null)//左子树为空,右子树不为空
				{
					if(temp.parent.right==temp)
					{
						temp.parent.right=temp.right;
						temp.right.parent=temp.parent;
					}else
					{
						temp.parent.left=temp.right;
						temp.right.parent=temp.parent;
					}
				}else if(temp.left!=null&&temp.right==null)//左子树不为空,右子树为空
				{
					if(temp.parent.right==temp)
					{
						temp.parent.right=temp.left;
						temp.left.parent=temp.parent;
					}
					else
					{
						temp.parent.left=temp.left;
						temp.left.parent=temp.parent;
					}
				}else//左右子树都不为空
				{
					TreeNode replacenode=minTreeNode(temp.right);
					delete(replacenode.data);
					temp.data=replacenode.data;
				}
				break;
			}
		}
	}
	
	public void getNodeInfo(TreeNode treenode)
	{
		if(treenode!=null)
		{
			System.out.print(treenode.data+" ");
		}
	}
}

class TreeNode{
	public int data;
	public TreeNode left;//左子节点
	public TreeNode right;//右子节点
	public TreeNode parent;//父节点
	//构造函数
	public TreeNode(int data)
	{
		this(data,null,null,null);
	}
	
	public TreeNode(int data,TreeNode left,TreeNode right,TreeNode parent)
	{
		this.data=data;
		this.left=left;
		this.right=right;
		this.parent=parent;
	}

}

运行结果如下所示:

递归方式实现前序遍历:
4 3 2 1 5 
递归方式实现中序遍历:
1 2 3 4 5 
递归方式实现后序遍历:
1 2 3 5 4 
非递归方式实现前序遍历:
4 3 2 1 5 
非递归方式实现中序遍历:
1 2 3 4 5 
非递归方式实现后序遍历:
1 2 3 5 4 

接下来,我们再给出自平衡二叉查找树的实现:

package mianshiti;

import java.util.Stack;

//AVL树的实现
public class AVLTree {
	AVLTreeNode rootnode=null;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		AVLTree mytree=new AVLTree();
		mytree.insert(4);
		mytree.insert(3);
		mytree.insert(2);
		mytree.insert(1);
		mytree.insert(5);
		System.out.println("递归方式实现前序遍历:");
		mytree.preorder(mytree.getRoot());
		System.out.println();
		System.out.println("递归方式实现中序遍历:");
		mytree.inorder(mytree.getRoot());
		System.out.println();
		System.out.println("递归方式实现后序遍历:");
		mytree.postorder(mytree.getRoot());
		System.out.println();
		System.out.println("非递归方式实现前序遍历:");
		mytree.nonRecursivePreorder(mytree.getRoot());
		System.out.println();
		System.out.println("非递归方式实现中序遍历:");
		mytree.nonRecursiveInorder(mytree.getRoot());
		System.out.println();
		System.out.println("非递归方式实现后序遍历:");
		mytree.nonRecursivePostorder(mytree.getRoot());
		System.out.println();
	}
	
	public AVLTreeNode getRoot()
	{
		return rootnode;
	}
	
	//前序遍历
	public void preorder(AVLTreeNode node)
	{
		if(node!=null)
		{
			this.getNodeInfo(node);
			preorder(node.left);
			preorder(node.right);
		}
	}
	//中序遍历
	public void inorder(AVLTreeNode node)
	{
		if(node!=null)
		{
			inorder(node.left);
			this.getNodeInfo(node);
			inorder(node.right);
		}
	}
	//后序遍历
	public void postorder(AVLTreeNode node)
	{
		if(node!=null)
		{
			postorder(node.left);
			postorder(node.right);
			this.getNodeInfo(node);
		}
	}
	//非递归方式实现前序遍历
	public void nonRecursivePreorder(AVLTreeNode node)
	{
		Stack<AVLTreeNode> mystack=new Stack<AVLTreeNode>();
		if(node!=null)
		{
			mystack.push(node);
			while(!mystack.isEmpty())
			{
				AVLTreeNode temp=mystack.pop();
				this.getNodeInfo(temp);
				if(temp.right!=null)
				{
					mystack.push(temp.right);
				}
				if(temp.left!=null)
				{
					mystack.push(temp.left);
				}
			}
		}
	}
	//非递归方式实现中序遍历
	public void nonRecursiveInorder(AVLTreeNode node)
	{
		Stack<AVLTreeNode> mystack=new Stack<AVLTreeNode>();
		while(node!=null)
		{
			while(node!=null)
			{
				if(node.right!=null)
				{
					mystack.push(node.right);
				}
				mystack.push(node);
				node=node.left;
			}
			node=mystack.pop();
			while(!mystack.isEmpty()&&node.right==null)
			{
				this.getNodeInfo(node);
				node=mystack.pop();
			}
			this.getNodeInfo(node);
			if(!mystack.isEmpty())
			{
				node=mystack.pop();
			}else
			{
				node=null;
			}
		}
	}
	//非递归方式实现后续遍历
	public void nonRecursivePostorder(AVLTreeNode node)
	{
		Stack<AVLTreeNode> mystack=new Stack<AVLTreeNode>();
		AVLTreeNode temp=node;
		while(node!=null)
		{
			for(;node.left!=null;node=node.left)
			{
				mystack.push(node);
			}
			while(node!=null&&(node.right==null||node.right==temp))
			{
				this.getNodeInfo(node);
				temp=node;
				if(mystack.isEmpty())
					return;
				node=mystack.pop();
			}
			mystack.push(node);
			node=node.right;
		}
	}
	
	public void getNodeInfo(AVLTreeNode node)
	{
		if(node!=null)
		{
			System.out.print(node.data+" ");
		}
	}

	public void insert(int data)
	{
		rootnode=insert(data,rootnode);
	}
	
	private AVLTreeNode insert(int data,AVLTreeNode root)
	{
		if(root==null)
		{
			AVLTreeNode node=new AVLTreeNode(data);
			return node;
		}
		if(data<root.data)
		{
			root.left=insert(data,root.left);
		}else if(data>root.data)
		{
			root.right=insert(data,root.right);
		}else
		{
			System.out.println("已存在"+data);
		}
		return balance(root);
	}
	
	//将二叉树平衡化
	private AVLTreeNode balance(AVLTreeNode root)
	{
		if(root==null)
			return root;
		if(height(root.left)-height(root.right)>1)
		{
			if(height(root.left.left)>=height(root.left.right))
			{
				root=rotateWithLeftChild(root);
			}else
			{
				root=doubleRotateLeftChild(root);
			}
		}else if(height(root.right)-height(root.left)>1)
		{
			if(height(root.right.right)>=height(root.right.left))
			{
				root=rotateWithRightChild(root);
			}else
			{
				root=doubleRotateRightChild(root);
			}
		}
		root.height=Math.max(height(root.left), height(root.right))+1;
		return root;
	}
	
	//单旋左子节点
	private AVLTreeNode rotateWithLeftChild(AVLTreeNode node)
	{
		AVLTreeNode root=node.left;
		node.left=root.right;
		root.right=node;
		node.height=Math.max(height(node.left),height(node.right))+1;
		root.height=Math.max(height(root.left),height(root.right))+1;
		return root;
	}
	
	//单旋右子节点
	private AVLTreeNode rotateWithRightChild(AVLTreeNode node)
	{
		AVLTreeNode root=node.right;
		node.right=root.left;
		root.left=node;
		node.height=Math.max(height(node.left),height(node.right))+1;
		root.height=Math.max(height(root.left),height(root.right))+1;
		return root;
	}
	
	//双旋左子节点
	private AVLTreeNode doubleRotateLeftChild(AVLTreeNode node)
	{
		node.left=rotateWithRightChild(node.left);
		return rotateWithLeftChild(node);
	}
	
	//双旋右子节点
	private AVLTreeNode doubleRotateRightChild(AVLTreeNode node)
	{
		node.left=rotateWithLeftChild(node.right);
		return rotateWithRightChild(node);
	}
	
	//返回一个节点的高度
	private int height(AVLTreeNode node)
	{
		return node==null?-1:node.height;
	}
}

//定义AVL树的节点
class AVLTreeNode
{
	int data;
	AVLTreeNode left;
	AVLTreeNode right;
	int height;
	
	//构造函数
	public AVLTreeNode(int data)
	{
		this(data,null,null);
	}
	
	public AVLTreeNode(int data,AVLTreeNode left,AVLTreeNode right)
	{
		this.data=data;
		this.left=left;
		this.right=right;
		this.height=0;
	}
}

运行结果如下所示:

递归方式实现前序遍历:
3 2 1 4 5 
递归方式实现中序遍历:
1 2 3 4 5 
递归方式实现后序遍历:
1 2 5 4 3 
非递归方式实现前序遍历:
3 2 1 4 5 
非递归方式实现中序遍历:
1 2 3 4 5 
非递归方式实现后序遍历:
1 2 5 4 3 





 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值