完整代碼在這裏 https://github.com/zhangjunapk/half_search_tree
樹被廣泛使用,比如文件系統,unix上用到了紅黑樹,windows上用到了樹
二分查找樹可以說是一個有序的集合,節點之間用鏈表鏈接起來,可以用二分搜索的方式來對搜索
二分查找樹在寫入上做了一個性能的權衡,每次寫入數據都要遍歷,然後放到合適的位置
是一種很平衡的數據存儲結構
先看看百度百科對二分搜索樹的定義把
只要滿足這些條件的樹都能被稱爲二分查找樹
比如可以是這樣子
這兩個都能被稱爲二分查找樹
接下來我們看看爲什麼二分查找樹爲什麼快
如果我們僅僅用鏈表的話,需要遍歷所有節點,直到找到值
加入我們查找6
傳統鏈表就需要順序遍歷節點,直到找到對應的值
但是二分搜索樹就不同了,在插入的時候就是排序好的,查找的話直接搜索就行了
在搜索的時候,會判斷當前數值是否一致,然後把要搜索的數值和當前節點進行大小對比,如果大於當前,那就從左邊繼續遞歸遍歷(左邊沒有就結束),右邊也是一樣,這樣就實現了二分查找(二分查找有序數組也有和當前節點對比的邏輯,然後切換索引),二分查找樹比數組方便的地方就是不用再進行一次排序了,因爲插入的時候就保證了順序性。當然這是有代價的,那就是插入的時候沒有數組性能高
接下來看插入代碼的邏輯
public void insert(String k,Integer v){
//先判斷root是否爲空
if(root==null){
root=new Node(k,v);
System.out.println("直接插到根 ");
return;
}
//接下來遍歷節點
Node node = new Node(k, v);
forInsert(node,root);
System.out.println("--------------------------------");
}
先看根節點是否爲空,如果爲空就直接初始化根節點,不然執行遞歸插入
//遞歸遍歷,然後插入數值
private int forInsert(Node insertNode, Node node) {
//百度百科上說鍵值不能相等
if(insertNode.getVal().equals(node.getVal()))
return -1;
//判斷都空的
if(node.getLeft()==null&&node.getRight()==null){
System.out.println("兩邊都空");
//判斷這個數值和當前節點的大小
if(insertNode.getVal()<=node.getVal()){
System.out.println(" 插入到"+node.getVal()+"的左邊"+insertNode.getVal());
node.setLeft(insertNode);
return 0;
}
if(insertNode.getVal()>=node.getVal()){
System.out.println(" 插入到右邊");
node.setRight(insertNode);
return 0;
}
}
//判斷要插入的值是否比當前的節點大
if(insertNode.getVal()<=node.getVal()){
if(node.getLeft()!=null){
return forInsert(insertNode,node.getLeft());
}
//不然直接insert
node.setLeft(insertNode);
return 0;
}
if(insertNode.getVal()>=node.getVal()){
if(node.getRight()!=null){
return forInsert(insertNode,node.getRight());
}
//不然直接insert
node.setRight(insertNode);
return 0;
}
return -1;
}
先判斷左右是否都爲空,爲空就把要插入的數值和當前的節點進行對比,然後直接set進去
然後就遞歸插入,進入下一層,然後接着判斷,直到左右都爲空,就接着判斷大小,然後set進去
數據插入進去了,我們就需要拿出來,這裏我寫了三種遍歷方式
1.先序遍歷(深度優先)
2.後序遍歷(深度優先)
3.按層遍歷(廣度優先)
先看廣度優先的
//按層遍歷
public void eachLayer(){
System.out.println(root.getVal());
layEach(root);
}
這裏先輸出根節點
然後調用遞歸遍歷層的方法
//廣度優先(按層遍歷裏面的遞歸)
private void layEach(Node node) {
if(node.getLeft()!=null){
System.out.println("L "+node.getLeft().getVal());
}
if(node.getRight()!=null){
System.out.println("R "+node.getRight().getVal());
}
if(node.getLeft()!=null){
layEach(node.getLeft());
}
if(node.getRight()!=null){
layEach(node.getRight());
}
}
這裏直接判斷是否爲空,然後直接打印
這裏我這樣寫是有原因的,因爲需要一層一層打印,所有就先輸出,然後進入下一層遞歸
這就是調用層級遍歷的輸出
然後是先序遍歷
//遞歸先序遍歷
private void eachBefore(Node node) {
System.out.println(node.getVal());
if(node.getLeft()!=null)
eachBefore(node.getLeft());
if(node.getRight()!=null)
eachBefore(node.getRight());
}
先序遍歷是從根節點開始遍歷,然後依次遍歷到左子樹的葉,然後纔會遍歷右子樹(層級調用真好玩)
接下來是後序遍歷
//遞歸後序遍歷
private void eachAfter(Node node) {
System.out.println(node.getVal());
if(node.getRight()!=null)
eachBefore(node.getRight());
if(node.getLeft()!=null)
eachBefore(node.getLeft());
}
這裏把先序遍歷換一下就行了,先遍歷右子樹,再遍歷左子樹
這是完整代碼 https://github.com/zhangjunapk/half_search_tree