BinarySearchTreeMap的實現
1 public interface Map<K extends Comparable<K>, V> { 2 void put(K k, V v); 3 4 V get(K k); 5 6 void delete(K k); 7 8 boolean contains(K k); 9 10 boolean isEmpty(); 11 12 int size(); 13 14 int size(K lo, K hi); 15 16 K min(); 17 18 K max(); 19 20 K floor(K k); 21 22 K ceiling(K k); 23 24 // the number of keys less than key 25 int rank(K k); 26 27 K select(int k); 28 29 void deleteMin(); 30 31 void deleteMax(); 32 33 // keys in [lo , hi] in sorted order 34 Iterable<K> keys(K lo, K hi); 35 36 Iterable<K> keys(); 37 }
二叉樹的定義
在計算機科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用於實現二叉查找樹和二叉堆。二叉樹的左子節點 < 父節點 < 右子節點
這是typical的二叉樹的樣子, null 代表子節點為空,從這張圖可以看出,左子節點 9 小於 父節點 10 小於 右子節點
1 private class Node<K, V> { 2 private K k; 3 private V v; 4 private Node<K, V> left; 5 private Node<K, V> right; 6 private int size; 7 Node(K k, V v) { this.k = k; this.v = v; } 8 Node(K k, V v, int size) { this.k = k; this.v = v; this.size = size;} 9 }
二叉樹的插入操作
假設我們依次插入 10 , 9, 15, 5 , 7 這5個元素到二叉樹中。see what will happen 這是個動態圖

1 @Override 2 public void put(K k, V v) { 3 root = put(root, k, v); //root 是根節點 4 } 5 6 private Node<K, V> put(Node<K, V> node, K k, V v) { 7 if (node == null) return new Node<>(k, v, 1); 8 int cmp = node.k.compareTo(k); 9 if (cmp > 0) { //node的k大一點 放到左邊的數中 10 node.left = put(node.left, k, v); 11 } else if (cmp < 0) { //node的k小一點 放到右邊的數中 12 node.right = put(node.right, k, v); 13 } else node.v = v; 14 15 node.size = size(node.left) + size(node.right) + 1; 16 return node; 17 }
二叉樹的get 方法
get方法簡單來說就是要找到那個key相同的對象。比如我們要在「10 , 9, 15, 5 , 7 」上圖所示中找到 7

1 @Override 2 public V get(K k) { 3 return get(root, k); 4 } 5
6 private V get(Node<K, V> node, K k) { 7
8 if (node == null) return null; //not find
9 else if (node.k.compareTo(k) > 0) { //node的k大一點 放到左邊的數中
10 return get(node.left, k); 11 } else if (node.k.compareTo(k) < 0) { //node的k小一點 放到右邊的數中
12 return get(node.right, k); 13 } else { //equal
14 return node.v; 15 } 16
17 }
二叉樹的刪除操作
其實想象一下,當你刪除一個node的時候,你需要找一個替代node來代替這個node。
這里又分3種情況。首先假設你有如下的樹結構

1.第一種情況是這個刪除的節點的左右節點都是null。
比如我要刪除3節點。其實只要直接把3節點reset 為null 就可以了。變成如下

2.第二種情況是刪除的節點的2個子節點中有一個子節點為null
比如我要刪除15。 15 的左節點是12 右節點是 null,所以符合這個情況
這個時候只需要直接把需要刪除的節點 reset 為 非空的子節點就可以了
所以在這里只需要把15的值替代為12

3.第三種情況是刪除的節點的2個子節點都不為null,
這個時候其實可以有2個選擇,一個是把刪除的節點替換為右子節點為根節點的那個樹中最小的節點
比如我要刪除10, 右節點為15(二叉樹的刪除操作的那個圖,不是上面的那個圖),15這個節點為根節點的樹中總共有2個元素(15和12),12是最小的。所以把需要刪除的節點替換為12。刪除后如下

另外一種選擇是把左節點為根節點的樹中最大的值取出來,把需要刪除的那個節點替換為這個左節點最大的元素(2個選擇沒什么區別)
1 @Override 2 public void delete(K k) { 3 delete(root, k); 4 } 5 //delete the k in the node tree and reset the size prorperty of this tree and subtrees to correct value 6 private Node<K, V> delete(Node<K, V> node, K k) { 7 if (node == null) return null; //沒有找到這個node 8 9 int cmp = node.k.compareTo(k); 10 if (cmp > 0) { 11 node.left = delete(node.left, k); 12 node.size = size(node.left) + size(node.right) + 1; 13 return node; 14 } else if (cmp < 0) { 15 node.right = delete(node.right, k); 16 node.size = size(node.left) + size(node.right) + 1; 17 return node; 18 } else { //hit the key 19 if (node.right == null) //if the right node is null then just replace this node with left node 20 return node.left; 21 else if (node.left == null) // if the left node is null then just replace this node with right node 22 return node.right; 23 else { 24 return deleteMin(node.right); // if both the subnodes are not null replace this node with the smallest node in the right sub node 25 } 26 } 27 } 28 29 //刪除從參數node開始的最小的node 30 private Node<K, V> deleteMin(Node<K, V> node) { 31 return delete(node, min(node)); 32 } 33 34 private Node<K, V> deleteMax(Node<K, V> node) { 35 return delete(node, max(node)); 36 } 37 38 @Override 39 public void deleteMin() { 40 deleteMin(root); 41 } 42 43 @Override 44 public void deleteMax() { 45 deleteMax(root); 46 } 47 48 @Override 49 public K min() { 50 return min(root); 51 } 52 53 //get the smallest node in the given node 54 private K min(Node<K, V> node) { 55 if (node == null) return null; 56 for (; node.left != null; node = node.left); 57 return node.k; 58 } 59 60 @Override 61 public K max() { 62 return max(root); 63 } 64 //get the most max node in the given node 65 private K max(Node<K, V> node) { 66 if (node == null) return null; 67 for (node = root; node.right != null; node = node.right); 68 return node.k; 69 }
分析
BinarySearchTree 有一個最大的缺點,就是如果插入的元素是ordered,比如我插入 1 2 3 4 5 6 這樣子,元素都會排在一邊。這樣子查找起來路徑很長,效率很低。
如果插入的元素是隨機的,那么所有的get put 操作的時間復雜度應該是 和 log2(N) 成正比的
具體的實現可以參考這個。https://github.com/Cheemion/algorithms/blob/master/src/com/algorithms/tree/BinarySearchTreeMap.java
有什么錯誤的地方歡迎大家指正哈
