1 package 查找;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 public class BST<Key extends Comparable<Key>, Value> {
7 private class Node {
8 private Key key; // 键
9 private Value value;// 值
10 private Node left, right; // 指向子树的链接
11 private int n; // 以该节点为根的子树中的节点总数
12
13 public Node(Key key, Value val, int n) {
14 this.key = key;
15 this.value = val;
16 this.n = n;
17 }
18 }
19
20 private Node root;
21
22 public int size() {
23 return size(root);
24 }
25
26 private int size(Node x) {
27 if (x == null)
28 return 0;
29 else
30 return x.n;
31 }
32
33 /**
34 * 如果树是空的,则查找未命中 如果被查找的键小于根节点,则在左子树中继续查找 如果被查找的键大于根节点,则在右子树中继续查找
35 * 如果被查找的键和根节点的键相等,查找命中
36 *
37 * @param key
38 * @return
39 */
40 public Value get(Key key) {
41 return get(root, key);
42 }
43
44 private Value get(Node x, Key key) {
45 if (x == null)
46 return null;
47 int cmp = key.compareTo(x.key);
48 if (cmp < 0)
49 return get(x.left, key);
50 else if (cmp > 0)
51 return get(x.right, key);
52 else
53 return x.value;
54 }
55
56 /**
57 * 二叉查找树的一个很重要的特性就是插入的实现难度和查找差不多。
58 * 当查找到一个不存在与树中的节点(null)时,new 新节点,并将上一路径指向该节点
59 *
60 * @param key
61 * @param val
62 */
63 public void put(Key key, Value val) {
64 root = put(root, key, val);
65 }
66
67 private Node put(Node x, Key key, Value val) {
68 if (x == null)
69 return new Node(key, val, 1);
70 int cmp = key.compareTo(x.key);
71 if (cmp < 0)
72 x.left = put(x.left, key, val);
73 else if (cmp > 0)
74 x.right = put(x.right, key, val);
75 else
76 x.value = val;
77 x.n = 1 + size(x.left) + size(x.right); // 要及时更新节点的子树数量
78 return x;
79 }
80
81 public Key min() {
82 return min(root).key;
83 }
84
85 private Node min(Node x) {
86 if (x.left == null)
87 return x;
88 return min(x.left);
89 }
90
91 public Key max() {
92 return max(root).key;
93 }
94
95 private Node max(Node x) {
96 if (x.right == null)
97 return x;
98 return max(x.right);
99 }
100
101 /**
102 * 向下取整:找出小于等于该键的最大键
103 *
104 * @param key
105 * @return
106 */
107 public Key floor(Key key) {
108 Node x = floor(root, key);
109 if (x == null)
110 return null;
111 else
112 return x.key;
113 }
114
115 /**
116 * 如果给定的键key小于二叉查找树的根节点的键,那么小于等于key的最大键一定出现在根节点的左子树中
117 * 如果给定的键key大于二叉查找树的根节点,那么只有当根节点右子树中存在大于等于key的节点时,
118 * 小于等于key的最大键才会出现在右子树中,否则根节点就是小于等于key的最大键
119 *
120 * @param x
121 * @param key
122 * @return
123 */
124 private Node floor(Node x, Key key) {
125 if (x == null)
126 return null;
127 int cmp = key.compareTo(x.key);
128 if (cmp == 0)
129 return x;
130 else if (cmp < 0)
131 return floor(x.left, key);
132 else {
133 Node t = floor(x.right, key);
134 if (t == null)
135 return x;
136 else
137 return t;
138 }
139 }
140
141 /**
142 * 向上取整:找出大于等于该键的最小键
143 *
144 * @param key
145 * @return
146 */
147 public Key ceiling(Key key) {
148 Node x = ceiling(root, key);
149 if (x == null)
150 return null;
151 else
152 return x.key;
153 }
154
155 /**
156 * 如果给定的键key大于二叉查找树的根节点的键,那么大于等于key的最小键一定出现在根节点的右子树中
157 * 如果给定的键key小于二叉查找树的根节点,那么只有当根节点左子树中存在大于等于key的节点时,
158 * 大于等于key的最小键才会出现在左子树中,否则根节点就是大于等于key的最小键
159 *
160 * @param x
161 * @param key
162 * @return
163 */
164 private Node ceiling(Node x, Key key) {
165 if (x == null)
166 return null;
167 int cmp = key.compareTo(x.key);
168 if (cmp == 0)
169 return x;
170 else if (cmp > 0) {
171 return ceiling(x.right, key);
172 } else {
173 Node t = floor(x.left, key);
174 if (t == null)
175 return x;
176 else
177 return t;
178 }
179 }
180
181 /**
182 * 选择排名为k的节点
183 *
184 * @param k
185 * @return
186 */
187 public Key select(int k) {
188 return select(root, k).key;
189 }
190
191 private Node select(Node x, int k) {
192 if (x == null)
193 return null;
194 int t = size(x.left);
195 if (t > k)
196 return select(x.left, k);
197 else if (t < k)
198 return select(x.right, k - t - 1);// 根节点也要排除掉
199 else
200 return x;
201 }
202
203 /**
204 * 查找给定键值的排名
205 *
206 * @param key
207 * @return
208 */
209 public int rank(Key key) {
210 return rank(key, root);
211 }
212
213 private int rank(Key key, Node x) {
214 if (x == null)
215 return 0;
216 int cmp = key.compareTo(x.key);
217 if (cmp < 0)
218 return rank(key, x.left);
219 else if (cmp > 0)
220 return 1 + size(x.left) + rank(key, x.right);
221 else
222 return size(x.left);
223 }
224 /**
225 * 删除最小键值对
226 */
227 public void deleteMin(){
228 root = deleteMin(root);
229 }
230 /**
231 * 不断深入根节点的左子树直到遇见一个空链接,然后将指向该节点的链接指向该结点的右子树
232 * 此时已经没有任何链接指向要被删除的结点,因此它会被垃圾收集器清理掉
233 * @param x
234 * @return
235 */
236 private Node deleteMin(Node x){
237 if(x.left == null) return x.right;
238 x.left = deleteMin(x.left);
239 x.n = 1 + size(x.left)+size(x.right);
240 return x;
241 }
242
243 public void deleteMax(){
244 root = deleteMax(root);
245 }
246 private Node deleteMax(Node x){
247 if(x.right == null ) return x.left;
248 x.right = deleteMax(x.right);
249 x.n = size(x.left)+size(x.right) + 1;
250 return x;
251 }
252
253 public void delete(Key key){
254 root = delete(root,key);
255 }
256 private Node delete(Node x, Key key){
257 if(x == null) return null;
258 int cmp = key.compareTo(x.key);
259 if(cmp < 0) x.left = delete(x.left,key);
260 else if(cmp > 0) x.right = delete(x.right,key);
261 else{
262 if(x.right == null) return x.left;
263 if(x.left == null ) return x.right;
264 /**
265 * 如果被删除节点有两个子树,将被删除节点暂记为t
266 * 从t的右子树中选取最小的节点x,将这个节点x的左子树设为t的左子树
267 * 这个节点x的右子树设为t的右子树中删除了最小节点的子树,这样就成功替换了t的位置
268 */
269 Node t = x;
270 x = min(t.right);
271 x.right = deleteMin(t.right);
272 x.left = t.left;
273 }
274 x.n = size(x.left) + size(x.right) +1;
275 return x;
276 }
277
278 public String toString(){
279 StringBuilder sb = new StringBuilder();
280 toString(root,sb);
281 sb.deleteCharAt(sb.length()-1);
282 return sb.toString();
283 }
284 private void toString(Node x, StringBuilder sb){
285 if(x == null ) return;
286 toString(x.left,sb);
287 sb.append("<"+x.key+","+x.value+">,");
288 toString(x.right,sb);
289 }
290
291 public List<Key> keys(){
292 return keys(min(),max());
293 }
294 public List<Key> keys(Key lo, Key hi){
295 List<Key> list = new ArrayList<Key>();
296 keys(root, list, lo, hi);
297 return list;
298 }
299 private void keys(Node x, List<Key> list, Key lo, Key hi){
300 if(x == null) return;
301 int cmplo = lo.compareTo(x.key);
302 int cmphi = hi.compareTo(x.key);
303 if(cmplo < 0 ) keys(x.left,list,lo,hi);
304 if(cmplo <= 0 && cmphi >= 0) list.add(x.key);
305 if(cmphi > 0 ) keys(x.right,list,lo,hi);
306 }
307 public static void main(String[] args){
308 BST<Integer,String> bst = new BST<Integer,String>();
309 bst.put(5, "e");
310 bst.put(1, "a");
311 bst.put(4, "d");
312 bst.put(9, "i");
313 bst.put(10, "j");
314 bst.put(2, "b");
315 bst.put(7, "g");
316 bst.put(3, "c");
317 bst.put(8, "h");
318 bst.put(6, "f");
319 List<Integer> keys = bst.keys();
320 for(int key : keys){
321 System.out.print("<"+key+","+bst.get(key)+">,");
322 }
323 System.out.println();
324 bst.deleteMin();
325 System.out.println(bst.toString());
326 bst.deleteMax();
327 System.out.println(bst.toString());
328 bst.delete(7);
329 System.out.println(bst.toString());
330 }
331 }
树还是应该做成可视化的方便查看和调试,后续我将更新一个可视化的生成图的版本出来,恩,一定要记得这件事