1. 程式人生 > >Java實現平衡二叉樹(AVLTree)的構建

Java實現平衡二叉樹(AVLTree)的構建

        最近在學習資料結構上關於平衡二叉樹的知識,看了嚴老師的思路,感覺用java寫出遞迴的構建方式有點困難,因為其中的遞迴需要把引用傳進去,所以感覺是要實現起來比較麻煩,所以就首先想到使用非遞迴的方式來實現構建平衡二叉樹。

       使用非遞迴的方式,思路也很簡單,就是為每一個結點都要定義一個平衡因子的屬性,當成功向樹中插入一個數據時,我就要進行回溯,看看有沒有平衡因子的絕對值等於2的結點,如果有,那就需要進行旋轉。當時的思路僅限於這些,接觸java沒有多久,目測如果實現起來,有點困難。

      其中,我認為還有一個難題就是,我定義的結點使用了泛型,即結點的value也是使用的泛型<E>,所以在比較大小的時候,我就無從下手,可能是我接觸java不太久的原因,當我參考上述部落格的思路後,居然是這樣實現的。

void compare(E element, Node node){
 	Comparable<? super E> e = (Comparable<? super E>)element;
 	int cmp = e.compareTo(node.element);
 	if(cmp > 0) //大於
 	else if(cmp == 0) //等於
 	else //小於
}

     通過此次學習,雖然接觸了平衡二叉樹的構建,但還是感覺學到了不少的知識,並且對java的使用又有了更多的認識。

     有了上述的感悟,感覺還是寫一個部落格來收藏一下,等自己有時間,再補充一下結點刪除的程式碼。

      可能本人程式碼在註釋上提供的思路有限,如果有看不懂的地方,可以參考上面的部落格地址的內容。

package util;

/** 
 * 平衡二叉樹 
 * 定義:首先它是一種特殊的二叉排序樹,其次它的左子樹和右子樹都是平衡二叉樹, 
 * 且左子樹和右子樹的深度之差不超過1 
 * 平衡因子:可以定義為左子樹的深度減去右子樹的深度 
 *  
 * 平衡二叉樹是對二叉排序樹的優化,防止二叉排序樹在最壞情況下平均查詢時間為n, 
 * 二叉排序樹在此時形如一個單鏈表 
 * 平衡二叉樹查詢元素的次數不超過樹的深度,時間複雜度為logN 
 */ 

public class AVLTree<E> {
	
	/**根節點*/
	private Node<E> root = null;
	
	/**樹中元素的個數*/
	private int size = 0;
	
	
	private static final int LEFT_HIGH = 1;
	private static final int RIGHT_HIGH = -1;
	private static final int EQUAL_HIGH = 0;
	
	public AVLTree(){}
	
	
	
	public boolean insertElement(E element){
		
		Node<E> t = root;
		if(t == null){
			/**將值複製給根節點,其父節點為空*/
			root = new Node(element, null);
			size = 1;
			return true;
		}
		
		int cmp = 0;
		Node<E> parent; /**用來儲存t的父節點*/
		
		/**查詢結點應存放的位置*/
		Comparable<? super E> e = (Comparable<? super E>)element;
		do{
			parent = t;
			cmp = e.compareTo(t.element);
			if(cmp < 0){
				t = t.left;
			}else if(cmp > 0){
				t = t.right;
			}else{
				return false;
			}
		}while(t != null);
		
		/**將結點存放在相應的位置*/
		Node<E> child = new Node(element, parent);
		if(cmp < 0){
			parent.left = child;
		}else{
			parent.right = child;
		}
		
		
		/**開始回溯,為根節點修改balabce,以便進行相應的調整*/
		while(parent != null){
			cmp = e.compareTo(parent.element);
			if(cmp < 0){
				parent.balance ++;
			}else{
				parent.balance --;
			}
			
			if(parent.balance == 0){/**從這以上的結點都不需要修改了,也不需要旋轉了*/
				break;
			}
			
			if(Math.abs(parent.balance) == 2){
				fixAfterInsertion(parent);
				break;
			}
			parent = parent.parent;
		}
		size ++;
		return true;
	}
	
	
	
	
	private void fixAfterInsertion(Node<E> p) {
		if(p.balance == 2){
			leftBanance(p);
		}
		if(p.balance == -2){
			rightBalance(p);
		}
	}
	

	/**
	 * 左平衡操作,即結點t的不平衡是因為左子樹過深
	 * 
	 * 1、如果新的結點插入到p的左孩子的左子樹中,則直接進行右旋操作即可
	 * 				t								lc
	 * 			   /  \		右旋操作				   /  \
	 * 			  lc   rc		------------->		 lcl   t
	 * 			 /  \								 /	  /  \
	 * 			lcl  lcr						   lcll  lcr rc 
	 * 			/
	 *         lcll
	 *         
	 * 2、如果新的結點插入到p的左孩子的右子樹中,則需要進行分情況討論
	 * 
	 * 	情況a:當p的左孩子的右子樹根節點的balance = RIGHT_HIGH
	 * 
	 * 			1						1						 	4
	 * 		   /  \					   /  \						   /  \
	 *        2    6      左旋     	4    6                 右旋		2    1
	 *       /  \  		------->     /  \        -------->       /    /  \
	 *      3    4                  2    5                      3    5    6
	 *            \                /
	 *             5			  3
	 * 
	 * 
	 * 	情況b:當p的左孩子的右子樹根節點的balance = LEFT_HIGH
	 * 
	 * 			1						1						 	4
	 * 		   /  \					   /  \						   /  \
	 *        2    6      左旋     	4    6          右旋		2    1
	 *       /  \  		------->     /          -------->        / \    \
	 *      3    4                  2                           3   5    6
	 *          /                  / \
	 *         5   			  	  3   5
	 * 
	 * 	情況c:當p的左孩子的右子樹根節點的balance = EQUAL_HIGH
	 * 
	 * 			1						1						 	4
	 * 		   /  \					   /  \						   /  \
	 *        2    7      左旋     	 4    7          右旋		2     1
	 *       /  \  		------->     / \         -------->       / \   / \
	 *      3    4                  2   6                       3   5  6  7
	 *          / \                / \
	 *         5   6			  3	  5
	 * */


	private void leftBanance(Node<E> t) {
		
		Node<E> lc = t.left;
		switch(lc.balance){
		case LEFT_HIGH:		/**新結點插入到t的左孩子的左子樹上,需要單右旋處理*/
			lc.balance = EQUAL_HIGH;
			t.balance = EQUAL_HIGH;
			break;
			
		case RIGHT_HIGH:	/**新結點插入到t的左孩子的右子樹上,需要雙旋處理*/
			Node<E> rd = lc.right;
			switch(rd.balance){
			case LEFT_HIGH:
				lc.balance = EQUAL_HIGH;
				t.balance = RIGHT_HIGH;
				break;
				
			case RIGHT_HIGH:		
				lc.balance = LEFT_HIGH;
				t.balance = EQUAL_HIGH;
				break;
			case EQUAL_HIGH:
				t.balance = EQUAL_HIGH;
				lc.balance = EQUAL_HIGH;
				break;
			}
			rd.balance = EQUAL_HIGH;
			/**對t的左子樹進行左旋處理*/
			left_Rotate(t.left);
			/**對t進行右旋處理*/
			right_Rotate(t);
			break;
		}
	}

	/**
	 * 右平衡操作,即結點t的不平衡是因為右子樹過深
	 * 
	 * 1、如果新的結點插入到p的右孩子的右子樹中,則直接進行左旋操作即可
	 * 
	 * 			p											r
	 * 		  /   \										   /  \
	 * 		 l	   r			   左旋操作 				  p   rr
	 * 			 /   \			----------->             / \    \
	 * 			rl    rr								l   rl   rrr
	 * 				   \
	 * 					rrr
	 * 
	 * 
	 * 2、如果新的結點插入到p的右孩子的左子樹中,則需要進行分情況討論
	 * 
	 * 	情況a:當p的右孩子的左子樹根節點的balance = LEFT_HIGH
	 * 
	 *			1						1							4
	 *		   /  \					   /  \						   /   \
	 *		  2    3		  右旋	  2	   4		左旋		  1     3
	 *			  /  \	   ------->		  /  \		------->	 /  \     \
	 * 			 4	  5					 6    3					2	6  	   5	
	 * 			/							   \
	 * 		   6								5
	 * 
	 * 情況b:當p的右孩子的左子樹根節點的balance = RIGHT_HIGH
	 * 
	 *			1						1							4
	 *		   /  \					   /  \						   /   \
	 *		  2    3		  右旋	  2	   4			左旋		  1     3
	 *			  /  \	   ------->		     \		------->	 /     /  \
	 * 			 4	  5					      3					2	  6	   5	
	 * 			  \							 /  \
	 * 		   	   6						6    5
	 * 
	 * 
	 * 情況C:當p的右孩子的左子樹根節點的balance = EQUAL_HIGH
	 *			1						1							4
	 *		   /  \					   /  \						   /   \
	 *		  2    3		  右旋	  2	   4 		左旋		  1     3
	 *			  /  \	   ------->		  /  \		------->	 / \    / \
	 * 			 4	  5					 6    3					2	6  7   5	
	 * 			/ \							 /  \
	 * 		   6   7						7    5
	 * */
	private void rightBalance(Node<E> p) {
		Node<E> rc = p.right;
		switch(rc.balance){
		case RIGHT_HIGH:		/**新結點插入到t的右孩子的右子樹上,需要單左旋處理*/
			rc.balance = EQUAL_HIGH;
			p.balance = EQUAL_HIGH;
			break;
			
		case LEFT_HIGH:		/**新結點插入到t的右孩子的左子樹上,需要雙旋處理*/
			Node<E> ld = rc.left;
			switch(ld.balance){
			case LEFT_HIGH:
				p.balance = EQUAL_HIGH;
				rc.balance = RIGHT_HIGH;
				break;
			case RIGHT_HIGH:
				p.balance = LEFT_HIGH;
				rc.balance = EQUAL_HIGH;
				break;
			case EQUAL_HIGH:
				p.balance = EQUAL_HIGH;
				rc.balance = EQUAL_HIGH;
				break;
			}
			ld.balance = EQUAL_HIGH;
			/**對p的右子樹進行右旋處理*/
			right_Rotate(p.right);
			/**對p進行左旋處理*/
			left_Rotate(p);
			break;
		}
		
	}


	/**
	 * 左旋操作
	 * 			p											r
	 * 		  /   \										   /  \
	 * 		 l	   r			   左旋操作 				  p   rr
	 * 			 /   \			----------->             / \    \
	 * 			rl    rr								l   rl   rrr
	 * 				   \
	 * 					rrr
	 * */

	private void left_Rotate(Node<E> p) {
		if(p != null){
			Node<E> r = p.right;	/**獲得p的右子樹的根節點r*/
			
			p.right = r.left;		/**將r的左子樹轉接到p的右子樹上*/
			if(r.left != null){		/**如果r的左子樹不為空,將左子樹的父節點設定為p*/
				r.left.parent = p;
			}
			
			r.parent = p.parent;	/**修改r的父節點,修改為p的父節點*/
			if(p.parent == null){	/**如果p的父節點為null,那麼現在r就是根節點了*/
				root = r;
			}else if(p == p.parent.left){/**如果p為其父節點的左孩子,將其父節點的左孩子指向r*/
				p.parent.left = r;
			}else if(p == p.parent.right){/**如果p為其父節點的右孩子,將其父節點的右孩子指向r*/
				p.parent.right = r;
			}
			
			r.left = p;		/**將r的左孩子設定為p*/
			p.parent = r;	/**將p的父節點設定為r*/
		}
	}


	/**
	 * 右旋操作
	 * 
	 * 				p									l
	 * 			   /  \			 右旋操作				   /  \
	 * 			  l	   r		------------->		  ll   p
	 * 			 /  \								 /	  /  \
	 * 			ll   lr								lll	 lr   r
	 * 			/
	 *         lll
	 * */
	private void right_Rotate(Node<E> p) {
		
		if(p != null){
			Node<E> l = p.left;		/**獲取p的左孩子l*/
			
			p.left = l.right;		/**將l的右子樹變為p的左子樹*/
			if(l.right != null){	/**如果l的右子樹不為空,將其父節點設定為p*/
				l.right.parent = p;
			}
			
			l.parent = p.parent;	/**將r的父節點修改為p的父節點*/
			if(p.parent == null){	/**如果p的父節點為null,即l為root*/
				root = l;
			}else if(p == p.parent.left){	/**如果p為其父節點的左孩子,將p的父節點的左孩子指向l*/
				p.parent.left = l;
			}else if(p == p.parent.right){ /**如果p為其父節點的右孩子,將p的父節點的右孩子指向l*/
				p.parent.right = l;
			}
			
			l.right = p;		/**將l的右子樹變為p*/
			p.parent = l;		/**修改p的父節點為l*/
		}
	}
	



	/**中序非遞迴方式遍歷平衡二叉樹*/
	public void nrInOrderTraverse(){
		Stack<Node<E>> stack = new Stack<Node<E>>();
		Node<E> p = root;
		while(p != null || !stack.isEmpty()){
			while(p != null){
				stack.push(p);
				p = p.left;
			}
			p = stack.pop();
			System.out.println(p.element);
			p = p.right;
		}
	}
	






	/**平衡二叉樹的結點定義*/
	class Node<E>{
		E element;
		/**結點的平衡因子*/
		int balance = 0;	
		/**左孩子結點、右孩子結點、父節點*/
		Node<E> left;
		Node<E> right;
		Node<E> parent;
		
		public Node(){}
		public Node(E element, Node<E> parent){
			this.element = element;
			this.parent = parent;
		}
		
		public String toString(){
			return element + " BF=" + balance;
		}
		
	}
	
	public static void main(String[] args) {
		Integer[] num = {5,8,2,0,1, -2, -9, 100};
		AVLTree<Integer> avl = new AVLTree<Integer>();
		for(int i = 0; i < num.length; i++){
			avl.insertElement(num[i]);
		}
		avl.nrInOrderTraverse();
	}

}