1.什麼是二叉搜索樹?node
二叉搜索樹又稱爲二叉排序樹,它或者是一棵空樹,或者是具備一下性質的樹:算法
若它的左子樹不空,則左子樹上全部的結點的值均不大於它根結點的值;數組
若它的左子樹不空,則左子樹上全部的結點的值均不小於它根結點的值;數據結構
它的左右子樹也是二叉搜索樹。數據結構和算法
2.二叉搜素樹的由來與做用?this
假設咱們如今有一個數據集,且這個數據集是順序存儲的有序線性表,那麼查找能夠運用折半、插值、斐波那契二等查找算法來實現,可是由於有序,在插入和刪除操做上,就須要耗費大量的時間(需進行元素的移位),可否有一種既可使得插入和刪除效率不錯,又可高效查找的數據結構和算法呢?spa
首先解決一個問題,如何使插入時不移動元素,咱們能夠想到鏈表,可是要保證其有序的話,首先得遍歷鏈表尋找合適的位置,那麼又如何高效的查找合適的位置呢,可否能夠像二分同樣,經過一次比較排除一部分元素。那麼咱們能夠用二叉樹的形式,以數據集第一個元素爲根節點,以後將比根節點小的元素放在左子樹中,將比根節點大的元素放在左子樹中,在左右子樹中一樣採起此規則。code
例子:將{63,55,90,42,58,70,10,45,67,83}造成二叉樹blog
那麼在查找x時,若x比根節點小能夠排除右子樹全部元素,去左子樹中查找(相似二分查找),這樣查找的效率很是好,並且插入的時間複雜度爲O(h),h爲樹的高度,較O(n)來講效率提升很多。排序
故二叉搜索樹用做一些查找和插入使用比較高的場景。
3.二叉搜索樹的創建、查詢、最大最小關鍵字,前驅、後繼
先來一個二叉搜索樹的結點結構
class BiNode{ public BiNode left; public BiNode right; public BiNode parent; public int data; public BiNode(BiNode left, BiNode right, BiNode parent, int data){ this.left = left; this.right = right; this.parent = parent; this.data = data; } }
在由來之中已經提到,二叉搜索樹的創建,是經過一個一個的結點的插入來創建,每一個結點有四個域,左右孩子、雙親、數據。
插入算法:將要插入的結點x,與根節點進行比較,若小於則去到左子樹進行比較,若大於則去到右子樹進行比較,重複以上操做直到找到一個空位置用於放置該新節點
其中root爲該樹的根節點。
public void insert(BiNode x){ BiNode y = null; BiNode temp = root; while(temp != null){ y = temp; if(x.data < temp.data){ temp = temp.left; }else{ temp = temp.right; } } x.parent = y; //若該樹爲空樹可直接將x放置在根節點上 if(null == y){ root = x; }else if(x.data<y.data){//找到空位置後,進行插入 y.left = x; }else{ y.right = x; } }
創建:可想而知,簡單的方法就是遍歷數組,將每個元素插入到二叉搜索樹中便完成了創建操做
public BiSortTree(int[] arr,int n){ for(int i=0;i<n;i++){ BiNode node = new BiNode(null, null ,null, arr[i]); insert(node); } }
查詢:根據二叉搜索樹的性質,將需查找的x與根節點進行比較,若小於則在左子樹中繼續查詢,若大於則在右子樹中繼續查詢,直到查到元素或元素爲空
遞歸查找:
/** * 查找結點,經過遞歸 * @param x * @return */ public BiNode queryByRec(BiNode root, int x){ if (x == root.data || null == root){ return root; } else if(x < root.data) { return queryByRec(root.left, x); } else { return queryByRec(root.right, x); } }
非遞歸查找(能不用遞歸則不用遞歸,遞歸會隱式的建立棧,從而浪費內存空間):
/** * 查找結點,非遞歸,須要傳入查找根節點 * @param x * @return */ public BiNode query(BiNode root, int x){ while(null != root && root.data != x){ if(x < root.data){ root = root.left; }else{ root = root.right; } } return root; }
最大最小關鍵字:這個樹中的最大的元素,根據二叉搜索樹性質,最大關鍵字爲該樹的最右元素,最小關鍵字爲該樹的最左元素,故直接將這兩個元素搜索出來便可(經過傳入根節點可實現查找子樹的最大最小關鍵字元素)
/** * 查找最小關鍵字元素,即最左結點,須要傳入查找根節點 * @return */ public BiNode minNode(BiNode x){ while(x.left!=null){ x=x.left; } return x; } /** * 查找最大關鍵字元素,即最右結點,須要傳入查找根節點 * @return */ public BiNode maxNode(BiNode x){ while(x.right!=null){ x=x.right; } return x; }
查找後繼結點:若是咱們須要找到如下二叉搜索樹的後繼,根據須要考慮兩種狀況
1.若是該節點有右子樹,那麼它右子樹的最小關鍵字元素即是其後繼
2.若是該節點無右子樹,因爲後繼爲大於該元素的元素,那麼這個結點必在其後繼的左子樹上,又因爲後繼爲大於該元素的最小元素那麼這個後繼必然是該節點第一個擁有左孩子的的祖先,綜合一下:這個結點的後繼爲這個結點第一個有左孩子的祖先,且這個左孩子也是這個結點的祖先。
/** * 查找指定數字的後繼 * @return */ public BiNode queryFollow(int x){ //首先查找指定結點是否存在 BiNode node = query(root, x); //不存在返回null if(null == node){ return null; } //若該節點有右子樹,則其後繼爲其右子樹的最左結點 //若該節點無右子樹,則其後繼爲其第一個擁有左孩子的祖先,且這個左孩子也是該結點的祖先 BiNode p = null; if(node.right!=null){ return minNode(node.right); }else{ p = node.parent; while(null != p && node == p.right){ node = p; p=p.parent; } return p; } }
前驅與後繼是相互對應的,不妨本身思考一下,代碼以下:
public BiNode queryPre(int x){ BiNode node = query(root, x); BiNode p = null; if(null == node){ return null; } if(node.left!=null){ return maxNode(node.left); }else{ p = node.parent; while(p!=null&&node==p.left){ node=p; p=p.parent; } return p; } }