二叉搜索樹
/**
* 二叉搜索樹
*/
public class BinarySearchTree {
public BinaryTreeNode<Integer> root;
public int size;
public BinarySearchTree() {
this.size = 0;
}
//所謂生成二叉搜索樹,就是通過n次的插入結點來完成
public BinaryTreeNode generateBST(int[] arr){
BinarySearchTree tree = new BinarySearchTree();
BinaryTreeNode tmp = tree.root;
tmp = insertNode(tmp,arr[0]); //生成根節點,要接收新的引用
for(int i=1;i<arr.length;i++){ //從1開始
insertNode(tmp,arr[i]);
}
return tmp;
}
//二叉搜索樹的插入 (整形的插入測試)
public BinaryTreeNode insertNode(BinaryTreeNode node,int value){
if(node==null){ //插入根節點
node = new BinaryTreeNode(value);
return node; //返回新的引用
}
//插入的值比當前子樹的根結點要小(等),往左走
//(且要判斷左孩子是否為空,為空就表示可以直接插入---因為沒有左孩子,就沒有比他更小的了)
if(value<=(Integer) node.data&&node.lchild!=null){
insertNode(node.lchild,value);
}else {
//說明左孩子為null
if(value<=(Integer)node.data){
node.insertLeft(value); //插入左孩子
}else if(value>(Integer)node.data&&node.rchild!=null){
insertNode(node.rchild,value);
}else{
//說明右孩子為null
node.insertRight(value); //插入右孩子
}
}
return null;
}
//層序遍歷
public void levelTraverse() throws Exception {
BinaryTreeNode node = root;
CycleQueue queue = new CycleQueue(new BinaryTreeNode[100]);
queue.enQueue(node);
while (!queue.isEmpty()){
BinaryTreeNode current = (BinaryTreeNode) queue.deQueue();
System.out.printf("%5d",current.data);
if(current.lchild!=null){
queue.enQueue(current.lchild);
}
if(current.rchild!=null){
queue.enQueue(current.rchild);
}
}
}
}
測試:
/**
* 二叉搜索樹
*/
BinarySearchTree searchTree = new BinarySearchTree();
int array[] = {50,24,76,4,32,64,100};
int array2[] = {20,40,10,80,45,60,12};
searchTree.root = searchTree.generateBST(array2);
System.out.println();
System.out.println("層序遍歷二叉搜索樹:");
searchTree.levelTraverse();
}
總結:
簡單來說:從根節點出發,往哪里走的問題
插入結點,生成樹其實就是不斷的插入而成
loop(node,value):
- 當比根節點大(往右走)
- 往右走如果右孩子為空,則直接插入作為右孩子
- 如果右孩子不為空,則遞歸進右孩子處goto loop(node.rchild,value)。繼續做根節點的左右大小比較(決定往哪走)
- 比根節點小(往左走)
- 往左走若左孩子為空,則直接插入作為左孩子
- 如果左孩子不為空,則遞歸進左孩子goto loop(node.lchild,value)處。把這個左孩子當作根節點,繼續做左右大小比較(決定下一步往哪走)
查找操作
//二叉搜索樹的結點查找,判斷是否存在
public boolean findNode(BinaryTreeNode node,int value){
//比node.data小,往左走,且判斷左孩子是否為空
if(value<(Integer)node.data&&node.lchild!=null){
return findNode(node.lchild,value);
//表示由於左孩子為空而退出的if,且value仍然<node.data,所以表示找不到指定結點
}else if(value<(Integer)node.data){
return false;
}else if(value>(Integer)node.data&&node.rchild!=null){
return findNode(node.rchild,value);
}else if(value>(Integer)node.data){
return false;
}else{
//當上面的情況都不符合,就表示屬於value==node.data的情況,所以表示找到了,返回true
return true;
}
}
時間復雜度:O(log2n)
刪除操作
分析:
刪除操作分三種情況
- 刪除的結點無左,右孩子------可直接刪除該結點(比如parent.lchild=null即可,java中無需手動釋放資源,C和python都要釋放)
- 刪除的結點只有左或者右孩子中的一個---------首先判斷刪除節點是來自父節點的左孩子,還是右孩子。
- 被刪除的結點A是左孩子,且A結點只有左孩子,那么--- parent.lchild = A.lchild
- 如果被刪除結點B是右孩子,且B結點只有右孩子,那么---parent.rchild = B.rchild
- 刪除的結點既有左孩子,又有右孩子-----做法是找到被刪除的結點C,的右子樹中最小的結點值min_value
- C.value = min_value 替換值
- 根據這個min_value 在C.rchild為根結點的子樹中,刪除(這個刪除也就是我們定義的刪除操作,遞歸式的)這個min_value所在的結點
//二叉搜索樹的刪除指定結點
public boolean deleteNode(BinaryTreeNode node,int value,BinaryTreeNode parent){
/**
* 首先找出該結點以及記錄下他的雙親節點parent
* 1. 第一種情況:為葉子節點, 通過parent直接刪除該節點即可 parent.lchild = null
* 2. 第二種情況,刪除的結點有左孩子,或者右孩子,通過 如parent,parent.lchild = node.lchild,刪除
* 3. 第三種情況,刪除的結點有左孩子,也有右孩子,通過找到這個結點右子樹中最小的結點(find_right_min)替代掉被刪除的結點,且這個最小的結點要置為null
*/
if(value<(Integer)node.data&&node.lchild!=null){
return deleteNode(node.lchild,value,node);
}else if(value<(Integer)node.data){
return false; //表示沒有這個結點
}else if(value>(Integer)node.data&&node.rchild!=null){
return deleteNode(node.rchild,value,node);
}else if(value>(Integer)node.data){
return false;
}else{
//表示value == node.data,存在該結點
//1. 左右孩子都不存在
if(node.lchild==null&&node.rchild==null){
//1.1 被刪除節點是父節點的左孩子
if(node==parent.lchild){
parent.lchild = null;
return true;
//1.2 被刪除節點是父節點的右孩子
}else{
parent.rchild = null;
return true;
}
//2. 左孩子存在,右孩子不在
}else if(node.lchild!=null&&node.rchild==null){
//2.1 當前結點是父節點的左孩子
if(node==parent.lchild){
parent.lchild = node.lchild;
return true;
//2.2 當前節點是父節點的右孩子
}else{
parent.rchild = node.lchild;
return true;
}
//3. 右孩子存在,左孩子不在
}else if(node.lchild==null&&node.rchild!=null){
//2.1 當前結點是父節點的左孩子
if(node==parent.lchild){
parent.lchild = node.rchild;
return true;
//2.2 當前結點是父節點的右孩子
}else{
parent.rchild = node.rchild;
return true;
}
//4. 左孩子,右孩子都在 node.lchild!=null&&node.rchild!=null
}else{
//4.1 找到右子樹中最小的結點值
Integer min_value = find_min_value(node.rchild);
//4.2 替換最小值
node.data = min_value;
//4.3 刪除右子樹中的最小節點
deleteNode(node.rchild,min_value,node);
return true;
}
}
}
查找最小值
//內部方法,用於查找最小結點值
private Integer find_min_value(BinaryTreeNode node){
if(node==null){
return null;
}
if(node.lchild==null){
return (Integer)node.data;
}else{
return find_min_value(node.lchild);
}
}
測試:
int array3[] = {50,30,80,20,35,70,100,34,40,75,32,36};
BinarySearchTree st = new BinarySearchTree();
//注意,一定要接收這個返回的副本引用!!!
st.root = st.generateBST(array3);
System.out.println();
System.out.println("bst:");
st.levelTraverse();
//測試刪除結點
System.out.println();
System.out.println("刪除35結點: ");
st.deleteNode(st.root,35,st.root);
st.levelTraverse();
測試結果:

原樹:

刪除35后:
50
30 80
20 36 70 100
34 40 75
32
