樹集合了數組(查找速度快)和鏈表(插入、刪除速度快)的優勢java
二叉樹是一種特殊的樹,即:樹中的每一個節點最多隻能有兩個子節點node
二叉搜索樹是一種特殊的二叉樹,即:節點的左子節點的值都小於這個節點的值,節點的右子節點的值都大於等於這個節點的值算法
節點類:數組
public class Node { public int id; public String name; public Node leftChild; public Node rightChild; public Node(int id, String name) { this.id = id; this.name = name; } }
實現類(若是樹中容許存在重複數據,處理起來比較麻煩,故實現中不容許樹中存在重複數據,即節點的右子節點的值必須大於節點的值):post
搜索二叉樹有一個特色,即若是使用中序遍歷遍歷搜索二叉樹,將獲得包含搜索二叉樹中全部節點值的升序排序結果this
public class BinarySearchTree { public Node root; public Node find(int key){ if(root == null){ System.out.println("The tree is empty!"); return null; } Node current = root; while(current.id != key){ if(key > current.id) current = current.rightChild; else current = current.leftChild; if(current == null) return null; } return current; } public boolean insert(Node node){ if(root == null){ root = node; return true; } //樹中不容許插入重複的數據項 if(this.find(node.id) != null){ System.out.println("Node with id '" + node.id + "' has already existed!"); return false; } Node current = root; while(current != null){ if(node.id > current.id){ if(current.rightChild == null){ current.rightChild = node; return true; } current = current.rightChild; }else{ if(current.leftChild == null){ current.leftChild = node; return true; } current = current.leftChild; } } return false; } //前序遍歷 public void preorder_iterator(Node node){ System.out.print(node.id + " "); if(node.leftChild != null) this.preorder_iterator(node.leftChild); if(node.rightChild != null) this.preorder_iterator(node.rightChild); } //中序遍歷 //中序遍歷二叉搜索樹將會獲得包含二叉搜索樹 //全部數據項的有序數列 public void inorder_iterator(Node node){ if(node.leftChild != null) this.inorder_iterator(node.leftChild); System.out.print(node.id + " "); if(node.rightChild != null) this.inorder_iterator(node.rightChild); } //後序遍歷 public void postorder_iterator(Node node){ if(node.leftChild != null) this.postorder_iterator(node.leftChild); if(node.rightChild != null) this.postorder_iterator(node.rightChild); System.out.print(node.id + " "); } //獲取樹(子樹)中的最小節點 public Node getMinNode(Node node){ if(this.find(node.id) == null){ System.out.println("Node dosen't exist!"); return null; } if(node.leftChild == null) return node; Node current = node.leftChild; while(current.leftChild != null) current = current.leftChild; return current; } //獲取樹(子樹)中的最大節點 public Node getMaxNode(Node node){ if(this.find(node.id) == null){ System.out.println("Node dosen't exist!"); return null; } if(node.rightChild == null) return node; Node current = node.rightChild; while(current.rightChild != null) current = current.rightChild; return current; } //刪除節點須要分3種狀況進行討論 public boolean delete(int key){ if(root == null){ System.out.println("The tree is empty!"); return false; } Node targetParent = root; Node target = root; boolean isLeftChild = true; while(target.id != key){ if(key > target.id){ targetParent = target; target = target.rightChild; isLeftChild = false; }else{ targetParent = target; target = target.leftChild; isLeftChild = true; } if(target == null) break; } if(target == null){ System.out.println("Node dosen't exist!" + "Can not delete."); return false; } //被刪除節點爲葉子節點 if(target.leftChild == null && target.rightChild == null){ if(target.id == root.id){ root = null; return true; } if(isLeftChild) targetParent.leftChild = null; else targetParent.rightChild = null; } //被刪除節點有1個子節點 //被刪除節點只有右子節點 else if(target.leftChild == null && target.rightChild != null){ if(target.id == root.id){ root = root.rightChild; return true; } if(isLeftChild) targetParent.leftChild = target.rightChild; else targetParent.rightChild = target.rightChild; } //被刪除節點只有左子節點 else if(target.leftChild != null && target.rightChild == null){ if(target.id == root.id){ root = root.leftChild; return true; } if(isLeftChild) targetParent.leftChild = target.leftChild; else targetParent.rightChild = target.leftChild; } //被刪除節點有2個子節點 else{ Node followingNode = this.getFollowingNode(target); if(target.id == root.id) root = followingNode; else if(isLeftChild) targetParent.leftChild = followingNode; else targetParent.rightChild = followingNode; followingNode.leftChild = target.leftChild; followingNode.rightChild = target.rightChild; } return true; } //獲取被刪除節點的後續節點 private Node getFollowingNode(Node node2Del){ Node nodeParent = node2Del; //只有被刪除節點有左右子節點時,纔會調用該方法 //這裏直接調用rightChild是沒有問題的 Node node = node2Del.rightChild; while(node.leftChild != null){ nodeParent = node; node = node.leftChild; } if(node.id != node2Del.rightChild.id) nodeParent.leftChild = node.rightChild; else nodeParent.rightChild = node.rightChild; return node; } public static void main(String[] args) { //插入 BinarySearchTree bst = new BinarySearchTree(); Node n1 = new Node(20, "root"); Node n2 = new Node(10, "left"); Node n3 = new Node(30, "right"); bst.insert(n1); bst.insert(n2); bst.insert(n3); //遍歷 bst.preorder_iterator(bst.root); System.out.println(); bst.inorder_iterator(bst.root); System.out.println(); bst.postorder_iterator(bst.root); System.out.println(); //刪除 Node n4 = new Node(5, ""); Node n5 = new Node(15, ""); Node n6 = new Node(40, ""); Node n7 = new Node(35, ""); Node n8 = new Node(45, ""); bst.insert(n4); bst.insert(n5); bst.insert(n6); bst.insert(n7); bst.insert(n8); bst.inorder_iterator(bst.root); System.out.println(); bst.delete(20); bst.inorder_iterator(bst.root); System.out.println(); } }
執行結果:spa
20 10 30 10 20 30 10 30 20 5 10 15 20 30 35 40 45 5 10 15 30 35 40 45
二叉搜索樹的效率:code
樹的大部分操做須要從上至下一層層的查找樹的節點,對於一棵滿樹,大約有一半的節點處於最底層(最底層節點數 = 其它層節點數的和 + 1),故節點操做大約有一半須要找到最底層節點,大約有四分之一的節點處於倒數第二層,故節點操做大約有四分之一須要找到倒數第二層節點,依此類推blog
查找過程當中,須要訪問每一層的節點,故只要知道了查找的層數,就能知道操做所需的時間,若是節點總數爲N,層數爲L,L=log2(N+1)排序
若是爲查找操做或刪除操做,被操做的節點多是是樹的任意節點,故查找操做或刪除操做的時間複雜度爲:1/21*log2(N+1) + 1/22*log2(N/2+1) + ... + 1/2N*1
若是爲插入操做,因爲每次都在樹的最低層插入新的節點,故插入操做的時間複雜度爲:log2(N+1)
總的來講能夠認爲二叉搜索樹操做的時間複雜度爲爲O(logN)
若是樹不是一棵滿樹,則判斷起來比較複雜,可是若是層數相同,對滿樹的操做確定比對不滿樹的操做更耗時
對於一個含有10000個數據項的有序鏈表,查找操做平均須要比較5000次,對於一個含有10000個節點的二叉搜索樹,查找操做大約須要13次
對於一個含有10000個數據項的有序數組,插入操做平均須要移動5000次(對於比較次數,使用不一樣的算法比較次數並不相同),對於一個含有10000個節點的二叉搜索樹,插入操做只需大約13次比較就可找到待插入節點的插入位置,而且因爲該位置老是處於二叉搜索樹的最底層,並不須要移動其它的節點
能夠看出,二叉搜索樹集合了有序鏈表插入刪除效率高和有序數組查詢效率高的優勢