一、二叉查找樹的介紹
二叉查找樹(Binary Search Tree),又被稱爲二叉搜索樹或二叉排序樹,是一種特殊的二叉樹,利用它可以很方便的對樹中節點進行排序和檢索。
二叉查找樹需要滿足以下的性質:
- 若它的左子樹不爲空,則左子樹上的所有節點的值都小於根節點的值
- 若它的右子樹不爲空,則右子樹上的所有節點的值都大於根節點的值
- 它的左、右子樹也都是二叉查找樹
對於二叉查找樹,按照中序遍歷(左根右)就可以得到由小到大的序列。
二、二叉查找樹的實現
二叉查找樹與基於鏈表的二叉樹創建方法類似,不同之處在於添加了一個父節點,也就是說採用二叉樹的三叉鏈表存儲方法,每個節點包括left、right和parent指針,用於表示該節點的左右節點和父節點。二叉查找樹主要需要實現兩個功能:
- 添加元素:添加元素後二叉樹依然有序,依然是二叉查找樹。
- 刪除元素:刪除完成後二叉樹依然有序,依然是二叉查找樹。
1.添加元素
已知一個關鍵字值爲value的結點,若將其插入到二叉查找樹中,只要保證插入後仍符合二叉查找樹的定義即可。插入可以用下面的方法進行:
(1)若二叉排序樹是空樹,則value成爲二叉排序樹的根;
(2)若二叉排序樹非空,則將value與二叉排序樹的根進行比較。如果value的值小於根結點的值,則將左節點作爲新的當前節點,如果value的值大於根結點的值,則將右節點作爲新的當前節點。
(3)重複步驟2,直到找到合適的插入位置。
2.刪除元素
當程序從排序二叉樹中刪除一個節點之後,爲了讓它依然保持爲排序二叉樹,程序必須對該排序二叉樹進行維護。維護可分爲如下幾種情況:
(1)若被刪除的節點是葉子節點,則只需將它從其父節點中刪除即可。
(2)如果待刪除節點左子樹存在右子樹不存在,或者左子樹不存在右子樹存在,直接將其子樹中存在的一邊候補上來即可。
(3)若被刪除節點 P 的左、右子樹均非空,有兩種做法:
- 將PL設爲P的父節點Q的左或右子節點(取決於P是其父節點Q的左或右子節點),將PR設爲P節點的中序前驅節點S的右子節點(S是PL最右下的節點,也就是PL子樹中最大的節點)。
- 以P節點的中序前驅或後繼代替P所指節點,然後再從原排序二叉樹中刪去中序前驅或後繼節點。(也就是,用大於P的最小節點或小於P的最大節點代替P節點)。
實現代碼如下所示:
import java.util.ArrayList;
import java.util.List;
public class SortTree {
class Node{
private int data;
private Node parent;
private Node left;
private Node right;
public Node(int value){
this.data = value;
this.parent = null;
this.left = null;
this.right = null;
}
}
private Node root = null;
/**
* 返回根節點
* @return
*/
public Node getRoot(){
return root;
}
/**
* 判斷是否爲空
* @return
*/
public boolean isEmpty(){
return root==null;
}
/**
* 返回樹的深度
* @return
*/
public int deep(){
return deep(root);
}
private int deep(Node n){
if(n == null){
return 0;
}
if(n.left==null&&n.right==null){
return 1;
}
//遞歸方式
int deepLeft = deep(n.left);
int deepRight = deep(n.right);
return deepLeft>deepRight?deepLeft:deepRight;
}
//比較兩個值的大小,用於插入和刪除操作
public int compare(int a,int b){
return a>b?1:-1;
}
/**
* 添加新節點
* @param value 新節點的值
* @return 返回是否成功
*/
public boolean add(int value){
//新節點newNode初始化
Node newNode = new Node(value);
//如果root爲空,則新節點爲root節點
if(root == null){
root = newNode;
return true;
}
Node current = root;
Node parent = null;
int cmp = 0;
//遍歷找到合適的插入節點位置
do{
parent = current;
cmp = compare(value,current.data);
if(cmp == 1){
current = current.right;
}
else{
current = current.left;
}
}
while(current!=null);
//設置新節點
if(cmp == 1){
parent.right = newNode;
newNode.parent = parent;
}
else{
parent.left = newNode;
newNode.parent = parent;
}
return true;
}
/**
* 找到某個值的節點,用於刪除操作
* @param value 目標節點的值
* @return 返回節點
*/
public Node findNode(int value){
if(root == null){
return null;
}
Node curr = root;
while(curr!=null){
if(value<curr.data){
curr = curr.left;
}
else if(value>curr.data){
curr = curr.right;
}
else{
return curr;
}
}
return null;
}
public boolean del(int value){
Node target = findNode(value);
if(target == null){
return false;
}
//如果刪除的節點沒有左節點也沒有右節點
if(target.left==null&&target.right==null){
//如果節點爲根節點
if(target == root){
root = null;
}
else{
//要刪除的節點爲左節點
if(target == target.parent.left){
target.parent.left = null;
}
//要刪除的節點爲右節點
else{
target.parent.right = null;
}
}
}
//如果刪除的節點只有右節點
if(target.left==null && target.right!=null){
//要刪除的節點爲根節點
if(target == root){
root = target.right;
}
else{
//要刪除的節點爲左節點
if(target == target.parent.left){
target.parent.left = target.right;
}
//要刪除的節點爲右節點
else{
target.parent.right = target.right;
}
target.right.parent = target.parent;
}
}
//如果刪除的節點只有左節點
if(target.left!=null&&target.right==null){
if(target == root){
root = root.left;
}
else{
if(target == target.parent.left){
target.parent.left = target.left;
}
else{
target.parent.right = target.left;
}
target.left.parent = target.parent;
}
}
//如果刪除的節點包含左節點和右節點
//以P節點的中序前驅代替P所指節點,然後再從原排序二叉樹中刪去中序前驅節點,用大於P的最小節點代替P節點
if(target.left!=null&&target.right!=null){
//leftMaxNode用於保存左子樹中最大的節點
Node leftMaxNode = target.left;
//遍歷右節點,找到值最大的節點
while(leftMaxNode.right!=null){
leftMaxNode = leftMaxNode.right;
}
//如果target.left沒有右節點
if(leftMaxNode == target.left){
leftMaxNode.parent.left = null;
}
//如果找到了最大的右節點
else{
leftMaxNode.parent.right = null;
}
//左子樹的最大節點指向目標節點的父節點
leftMaxNode.parent = target.parent;
if(target ==target.parent.left){
//如果目標節點爲左節點,左節點指向leftMaxNode
target.parent.left = leftMaxNode;
}
else{
//如果目標節點爲右節點,右節點指向leftMaxNode
target.parent.right = leftMaxNode;
}
//leftMaxNode替代target的位置
leftMaxNode.left = target.left;
leftMaxNode.right = target.right;
//target置空
target.parent = target.left = target.right = null;
}
return true;
}
/**
* 中序遍歷
* @return 返回存儲Node節點的list
*/
public List<Node> inIterator(){
return inIterator(root);
}
public List<Node> inIterator(Node n){
List<Node> list = new ArrayList<Node>();
//遞歸方式
if(n.left!=null){
list.addAll(inIterator(n.left));
}
list.add(n);
if(n.right!=null){
list.addAll(inIterator(n.right));
}
return list;
}
public static void main(String args[]){
SortTree st = new SortTree();
/**
* 1
* 8
* 5 10
* 2 7 9 11
* 3 6
*/
st.add(1);
st.add(8);
st.add(5);
st.add(2);
st.add(7);
st.add(10);
st.add(9);
st.add(11);
st.add(6);
st.add(3);
st.del(10);
List<SortTree.Node> inlist = new ArrayList<SortTree.Node>();
inlist = st.inIterator();
for(SortTree.Node n:inlist){
System.out.print(n.data + " ");
}
System.out.println();
}
}
測試結果:
1 2 3 5 6 7 8 9 11