1. 程式人生 > >Java 實現二叉搜尋樹的建立、查詢、插入、刪除結點

Java 實現二叉搜尋樹的建立、查詢、插入、刪除結點

二叉搜尋樹特點:左孩子結點值比當前結點值小,右孩子結點值比當前值大或等於當前值。
本文假設樹中的結點值不存在相同的兩項或多項。

一、二叉搜尋樹的建立

 1、首先定義結點類

    程式碼如下:

class TreeNode{
	public int iData;          //結點的資料項
	public TreeNode leftChild; //結點指向左孩子的引用
	public TreeNode rightChild;//結點指向右孩子的引用
	
	public TreeNode(int iData){//構造方法,初始化結點資料項
		this.iData = iData;
	}
	public void displayNode(){ //輸出結點值
		System.out.print(" "+this.iData);
	}
}
2、定義二叉搜尋樹類

程式碼如下:

public class BinarySearchTree {//二叉搜尋樹特點:左孩子結點值比當前結點值小,右孩子結點值比當前值大或等於當前值;
	public TreeNode root;  //定義樹的根結點
	
	public BinarySearchTree(int iData){//構造方法用以初始化根結點值;
		root = new TreeNode(iData);
	}
	public TreeNode find(int iData){}         //查詢指定結點方法
	public TreeNode getParentNode(int iData){}//獲取當前值結點的父結點方法
	public TreeNode findMin(TreeNode root){}  //查詢樹的最小值結點
	public TreeNode findMax(TreeNode root){}  //查詢樹的最大值結點
	public TreeNode findInorderSuccessor(TreeNode node){}//查詢當前結點的-右子樹的-後繼中序結點[即當前結點的右子樹按照中序遍歷獲取的第一個結點]
	public void deleteNode(int key){}                    //刪除指定值的結點
	public void insert(int iData){}                      //插入指定值的結點
	public void inOrder(TreeNode node){}                 //中序遍歷二叉搜尋樹的遞迴實現;
	public void preOrder(TreeNode node){}                //先序遍歷二叉搜尋樹的遞迴實現;
	public void postOrder(TreeNode node){}               //後序遍歷二叉搜尋樹的遞迴實現;
}
3、實現insert方法進行樹的建立
	public void insert(int iData){
		TreeNode node = new TreeNode(iData);
		TreeNode current ;
		if(root == null){
			root = node;
			return;
		}
		else{
			current = root;
			while(true){
				if(iData >= current.iData){
					if(current.rightChild == null){
						current.rightChild = node;
						return;/////找到插入位置就插入,並且返回不再執行
					}else{
						current = current.rightChild;
					}
				}else{
					if(current.leftChild == null){
						current.leftChild = node;
						return;////找到插入位置就插入,並且返回不再執行
					}else{
						current = current.leftChild;
					}
				}// end if
			}// end while
		}// end root is not null
	}
二、刪除樹中的指定結點

刪除結點操作是最複雜的,總體上分為四種情況:1.該結點的左右子結點均為空;2.該結點的左子結點不為空,右子結點為空;3.該結點的右子結點不為空,左子結點為空;4.該結點的左右子結點均不為空。下面分別對上述情況進行討論。

1.該結點的左右子結點均為空;

這種情況細分,又分為3種情況:A:該結點的父結點為空,說明該結點為根結點,這種情況直接將根結點置空即可。B:該結點是父結點的左孩子結點。C:該結點是父結點的右孩子結點。

程式碼實現:

	if(keyNode.leftChild == null && keyNode.rightChild == null){//若該結點是葉子結點
		if(parent == null){//若該結點是根結點
			this.root = null;
			return;
		}
		if(parent.leftChild == keyNode){
			parent.leftChild = null;
		}else{
			parent.rightChild = null;
		}
		return;
	}
2.該結點的左子結點不為空,右子結點為空;

這種情況也比較簡單,只需要同上,類似地判定該結點是其父結點的左子結點還是右子結點進行處理即可。

else if(keyNode.rightChild == null ){   //若該結點的左子樹不空
	if(parent.leftChild == keyNode){
		parent.leftChild = keyNode.leftChild;
	}else{
	        parent.rightChild = keyNode.leftChild;
	}
	return;
}
3.該結點的右子結點不為空,左子結點為空;

類似地,同上:

else if(keyNode.leftChild == null ){  //若該結點的右子樹不空
	if(parent.leftChild == keyNode){
		parent.leftChild = keyNode.rightChild;
	}else{
		parent.rightChild = keyNode.rightChild;
	}
	return;
 }
4.該結點的左右子結點均不為空。

這種情況最為複雜,又細分為兩種情況:A、該結點右子樹中的中序後繼結點是該結點的右子結點;B、該結點的右子樹中的中序後繼結點是該結點的右子樹的左孫子結點。所謂中序後繼結點:

即當前結點的右子樹按照中序遍歷獲取的第一個結點。也就是第一個比該結點大的結點,我們用這個結點來代替被刪除的結點,以保證對指定結點刪除後樹的有序性。
A種情況:
	if(inorderSuccessor == keyNode.rightChild){            //若該結點右子樹中的中序後繼結點是該結點的右子結點;
		inorderSuccessor.leftChild = keyNode.leftChild;//將後繼結點的左孩子指向被刪除結點的左孩子結點;
		if(parent.leftChild == keyNode){               //判斷被刪除結點是其父結點的左孩子還是右孩子;
			parent.leftChild = inorderSuccessor;   //將父結點的左孩子指向後繼結點
		}else{
			parent.rightChild = inorderSuccessor;
		}
	}
B種情況,分四步:
	if(parent.leftChild == keyNode){
		parentSuccessor.leftChild = inorderSuccessor.rightChild;//將後繼結點的父結點的左孩子結點指向後繼結點的右孩子結點;
		inorderSuccessor.rightChild = keyNode.rightChild;       //將後繼結點的右孩子結點指向被刪除結點的右孩子結點
		parent.leftChild = inorderSuccessor;                    //將被刪除結點的父結點的左孩子結點指向後繼結點
		inorderSuccessor.leftChild = keyNode.leftChild;         //將後繼結點的左孩子結點指向被刪除結點的左孩子結點
	}else{
		parentSuccessor.leftChild = inorderSuccessor.rightChild;
		inorderSuccessor.rightChild = keyNode.rightChild;
		parent.rightChild = inorderSuccessor;
		inorderSuccessor.leftChild = keyNode.leftChild;
	}
以上是對二叉搜尋樹的簡單操作介紹,下面是完整程式碼和執行結果:
package test2;
class TreeNode{
	public int iData;
	public TreeNode leftChild;
	public TreeNode rightChild;
	
	public TreeNode(int iData){
		this.iData = iData;
	}
	public void displayNode(){
		System.out.print(" "+this.iData);
	}
}
public class BinarySearchTree {//二叉搜尋樹:左孩子結點值比當前結點值小,右孩子結點值比當前值大或等於當前值;
	public TreeNode root;
	
	public BinarySearchTree(int iData){
		root = new TreeNode(iData);
	}
	public TreeNode find(int iData){
		TreeNode current;
		current = root;
		while(current != null){
			if(iData < current.iData){
				current = current.leftChild;
			}else if(iData > current.iData){
				current = current.rightChild;
			}else{
				return current;
			}
		}
		return null;
	}
	public TreeNode getParentNode(int iData){
		TreeNode current;
		current = root;
		TreeNode parent = root;
		while(current != null){
			if(iData < current.iData){
				parent = current;
				current = current.leftChild;
			}else if(iData > current.iData){
				parent = current;
				current = current.rightChild;
			}else{
				if(current == root){
					return null;
				}
				else
					return parent;
			}
		}
		return null;
	}
	public TreeNode findMin(TreeNode root){
		TreeNode  node;
		node = root;
		while(node != null){
			if(node.leftChild != null){
				node = node.leftChild;
			}else{
				System.out.println("min:"+node.iData);
				return node;
			}
		}
		System.out.println("tree is null");
		return null;
	}
	public TreeNode findMax(TreeNode root){
		TreeNode  node;
		node = root;
		while(node != null){
			if(node.rightChild != null){
				node = node.rightChild;
			}else{
				System.out.println("max:"+node.iData);
				return node;
			}
		}
		System.out.println("tree is null");
		return null;
	}
	public TreeNode findInorderSuccessor(TreeNode node){
		TreeNode current = node.rightChild;
		while(current != null){
			if(current.leftChild != null){
				current = current.leftChild;
			}else
				return current;
		}
		return current;
	}
	public void deleteNode(int key){
		TreeNode keyNode = find(key);
		TreeNode parent = getParentNode(key);
		if(keyNode != null){//如果找到了要刪除的結點
			if(keyNode.leftChild == null && keyNode.rightChild == null){//若該結點是葉子結點
				if(parent == null){//若該結點是根結點
					this.root = null;
					return;
				}
				if(parent.leftChild == keyNode){
					parent.leftChild = null;
				}else{
					parent.rightChild = null;
				}
				return;
			}else if(keyNode.rightChild == null ){   //若該結點的左子樹不空
				if(parent.leftChild == keyNode){
					parent.leftChild = keyNode.leftChild;
				}else{
					parent.rightChild = keyNode.leftChild;
				}
				return;
			}else if(keyNode.leftChild == null ){  //若該結點的右子樹不空
				if(parent.leftChild == keyNode){
					parent.leftChild = keyNode.rightChild;
				}else{
					parent.rightChild = keyNode.rightChild;
				}
				return;
			}else{				//若該結點的左右子結點都不空:1、該結點右子樹中的中序後繼結點是該結點的右子結點;2、該結點的右子樹中的中序後繼結點是該結點的右子樹的左孫子結點;
				TreeNode inorderSuccessor = findInorderSuccessor(keyNode);
				TreeNode parentSuccessor = getParentNode(inorderSuccessor.iData);
				if(inorderSuccessor == keyNode.rightChild){//若該結點右子樹中的中序後繼結點是該結點的右子結點;
					inorderSuccessor.leftChild = keyNode.leftChild;
					if(parent.leftChild == keyNode){
						parent.leftChild = inorderSuccessor;
					}else{
						parent.rightChild = inorderSuccessor;
					}
				}else{										//若該結點的右子樹中的中序後繼結點是該結點的右子樹的左孫子結點;
					if(parent.leftChild == keyNode){
						parentSuccessor.leftChild = inorderSuccessor.rightChild;
						inorderSuccessor.rightChild = keyNode.rightChild;
						parent.leftChild = inorderSuccessor;
						inorderSuccessor.leftChild = keyNode.leftChild;
						//parent.leftChild = inorderSuccessor;
					}else{
						//parent.rightChild = inorderSuccessor;
						parentSuccessor.leftChild = inorderSuccessor.rightChild;
						inorderSuccessor.rightChild = keyNode.rightChild;
						parent.rightChild = inorderSuccessor;
						inorderSuccessor.leftChild = keyNode.leftChild;
					}
				}// end else
			}//end else:both is not null
		}//end if find keyNode
	}//end method
	public void insert(int iData){
		TreeNode node = new TreeNode(iData);
		TreeNode current ;
		if(root == null){
			root = node;
			return;
		}
		else{
			current = root;
			while(true){
				if(iData >= current.iData){
					if(current.rightChild == null){
						current.rightChild = node;
						return;/////
					}else{
						current = current.rightChild;
					}
				}else{
					if(current.leftChild == null){
						current.leftChild = node;
						return;////
					}else{
						current = current.leftChild;
					}
				}// end if
			}// end while
		}// end root is not null
	}
	public void inOrder(TreeNode node){//中序遍歷二叉搜尋樹的遞迴實現;
		if(node != null){
			inOrder(node.leftChild);
			System.out.print(" "+node.iData);
			inOrder(node.rightChild);
		}
	}
	public void preOrder(TreeNode node){
		if(node != null){
			System.out.print(" "+node.iData);
			preOrder(node.leftChild);
			preOrder(node.rightChild);
		}
	}
	public void postOrder(TreeNode node){
		if(node != null){
			postOrder(node.leftChild);
			postOrder(node.rightChild);
			System.out.print(" "+node.iData);
		}
	}

}

class theTreeApp{
	public static void main(String args[]){
		BinarySearchTree tree = new BinarySearchTree(-1);
		tree.insert(-2);
		tree.insert(1);
		tree.insert(0);
		tree.insert(4);
		tree.insert(2);
		tree.insert(5);
		tree.insert(3);
		
		tree.preOrder(tree.root);
		System.out.print("\n");
		
		tree.inOrder(tree.root);
		System.out.print("\n");
		
		tree.postOrder(tree.root);
		System.out.print("\n");
		
		tree.findMin(tree.root);
		tree.findMax(tree.root);
		
		tree.deleteNode(1);
		
		tree.preOrder(tree.root);
		System.out.print("\n");
		
		tree.inOrder(tree.root);
		System.out.print("\n");
	}
}
執行結果:
-1 -2 1 0 4 2 3 5
-2 -1 0 1 2 3 4 5
-2 0 3 2 5 4 1 -1
min:-2
max:5
-1 -2 2 0 4 3 5
-2 -1 0 2 3 4 5