[数据结构与算法]二叉排序(搜索)树实现
定义
二叉排序树又称二叉查找树或二叉搜索树,它或者是一棵空树,或者是具有如下性质的二叉树:
1、若它是左子树非空,则左子树上所有节点的值均小于根节点的值
2、若它的右子树非空,则右子树上所有节点的值均大于根节点的值
3、左、右子树本身就是两根二叉排序树
查找
因为二叉排序中左子树上所有节点关键字均小于根节点的关键字;右子树上所有节点的关键字均大于根节点的关键字,所以在二叉排序树上进行查找,与折半查找过程类似。
在二叉排序树上进行查找的过程为:若二叉排序树非空,将给定值与根节点的关键字值比较,若相等,则查找成功;若不等,则当根节点的关键字值大于给定值时,到根的左子树中进行查找;否则到根的右子树中进行查找。若找到,则查找过程是走了一条从树根到所找到节点的路径;否则,查找过程终止于一棵空树。
创建
每次插入的新节点都是二叉排序树上新的叶子节点,则在进行插入操作时,不必移动其他节点,仅需改动某个节点的指针。这就是相当于一个有序序列上插入一个记录而不需要移动其他记录。它表明,二叉排序树具有类似于折半查找的我,可采用链表存结构,因此是动态查找的一种适宜表示。
另外,由于一棵二叉排序树的形态完全由输入序列决定,所以在输入充已经有序的情况下,所构造的二叉排序树是一棵单枝树,从二叉树的查打过程可知,这种情况下的查找效率和顺序查找的效率是相同的。
删除
在查找二叉树上删除一个节点时,要考虑三种情况:
1、若待删除的节点P是叶子节点,则直接删除该节点;
2、若待删除的节点P只有一个子节点,则将这个节点与删除节点的父节点直接连接,然后删除节点P;
3、若待删除节点P有两个子节点时,应该使用中序遍历方式得到的直接前置节点S或直接后继节点S的值来代替点P的值,然后删除节点S,(注:节点S肯定属于上述1、2情况之一)。比如删除下50节点,可以使用直接前置节点45或直接后继节点65来替换它。注:这里到底是使用直接前置节点还是使用直接后继节点是有讲究的,因为迭代器的next与找替代元素可以使用一套共用的代码successor方法,而迭代当然是从前向后迭代(中序遍历顺序)是最自然的了,所以我们这里使用直接后继节点来替换。
对二叉搜索树的中序遍历将按照递增顺序访问树中的元素。二叉搜索树不允许树中的元素重复,所在要添加元素时要加以判断,如果已存在,则直接跳过并返回失败。
插入到二叉搜索树中的节点总是会成为树中的叶子节点,所有在插入元素之后,没有必要重组树。但删除一个节点却比插入一个节点复杂得多,如果删除的不是叶子节点,则删除后必须重组树。
实现
1 package tree.search; 2 3 /** 4 * 树节点访问接口 5 * @author jzj 6 * @date 2010-1-3 7 * @param <E> 8 */ 9 public interface TreeOrder<E extends Comparable<E>> { 10 11 static interface Visitor<E extends Comparable<E>> { 12 void visit(E ele); 13 } 14 15 //前序遍历 16 void preOrder(Visitor<E> v); 17 18 //中序遍历 19 void inOrder(Visitor<E> v); 20 21 //后序遍历 22 void postOrder(Visitor<E> v); 23 24 //层次遍历 25 void levelOrder(Visitor<E> v); 26 }
1 package tree.search; 2 3 import java.util.AbstractSet; 4 import java.util.Iterator; 5 import java.util.LinkedList; 6 import java.util.NoSuchElementException; 7 import java.util.Random; 8 9 /** 10 * 二叉搜索树(也叫二叉排序树)实现 11 * 12 * @author jzj 13 * @data 2009-12-18 14 * @param <E> 15 */ 16 public class BinSearchTree<E extends Comparable<E>> extends AbstractSet<E> implements 17 TreeOrder<E> { 18 private static class Entry<E> { 19 /* 20 * 注,内部类的字段一般定义成默认访问修饰符要好一点,因为这样外部类可以直接字段 21 * ,而不必要使用get、set这样做只是为了更简洁,而该内部类又是私有的,所以外面 22 * 即使是同包也是不可能直接访问到的 23 */ 24 E elem;//数据域 25 Entry<E> paraent;//父节点 26 Entry<E> left;//左节点 27 Entry<E> right;//右节点 28 29 //构造函数只有两个参数,左右节点是调用add方法时设置 30 public Entry(E elem, Entry<E> parent) { 31 this.elem = elem; 32 this.paraent = parent; 33 } 34 } 35 36 private Entry<E> root;//根节点 37 private int size;//树节点个数 38 39 public BinSearchTree() { 40 root = null; 41 } 42 43 //前序遍历 44 public void preOrder(Visitor<E> v) { 45 preOrder(root, v); 46 } 47 48 private final void preOrder(Entry<E> p, Visitor<E> v) { 49 if (p != null) { 50 v.visit(p.elem); 51 preOrder(p.left, v); 52 preOrder(p.right, v); 53 } 54 } 55 56 //中序遍历 57 public void inOrder(Visitor<E> v) { 58 inOrder(root, v); 59 } 60 61 private final void inOrder(Entry<E> p, Visitor<E> v) { 62 if (p == null) { 63 return; 64 } 65 inOrder(p.left, v); 66 v.visit(p.elem); 67 inOrder(p.right, v); 68 69 } 70 71 //后序遍历 72 public void postOrder(Visitor<E> v) { 73 postOrder(root, v); 74 75 } 76 77 private final void postOrder(Entry<E> p, Visitor<E> v) { 78 if (p == null) { 79 return; 80 } 81 82 postOrder(p.left, v); 83 postOrder(p.right, v); 84 v.visit(p.elem); 85 86 } 87 88 //层次 89 public void levelOrder(Visitor<E> v) { 90 if (root == null) { 91 return; 92 } 93 LinkedList<Entry<E>> queue = new LinkedList<Entry<E>>(); 94 queue.addLast(root); 95 while (!queue.isEmpty()) { 96 Entry<E> p = queue.removeFirst(); 97 v.visit(p.elem); 98 if (p.left != null) { 99 queue.add(p.left); 100 } 101 if (p.right != null) { 102 queue.add(p.right); 103 } 104 } 105 } 106 107 public int size() { 108 return size; 109 } 110 111 /** 112 * 是否含有某元素 113 * @param e 114 * @return boolean 115 */ 116 public boolean contanins(E e) { 117 Entry<E> tmp = root; 118 int comp; 119 while (tmp != null) {//如果树不为空 120 comp = e.compareTo(tmp.elem); 121 //如果与tmp元素相等,则返回 122 if (comp == 0) { 123 return true; 124 }//如果比tmp小,则在tmp的左子树中找 125 else if (comp < 0) { 126 tmp = tmp.left; 127 }//如果比tmp大,则在tmp的右子树中找 128 else { 129 tmp = tmp.right; 130 } 131 } 132 //树本身就为空或树不为空时没有找到时 133 return false; 134 } 135 136 /** 137 * 向二叉搜索树中添加节点 138 * 被插入的元素总是变成树中的叶结点,所以在插入元素后,没有必要重新组织树,这与AVL树或 139 * RED-BLACK树不太一样 140 * @param e 141 * @return boolean 142 */ 143 public boolean add(E e) { 144 //1、如果树为空,则直接加入 145 if (root == null) { 146 //根的父节点为null,也就是说只要是parent为null的节点就是根元素 147 root = new Entry<E>(e, null); 148 size++; 149 return true; 150 }//如果树不为空 151 else { 152 Entry<E> tmp = root; 153 int comp; 154 while (true) {//死循环,直到节点插入到正确位置或元素已存在 155 comp = e.compareTo(tmp.elem); 156 //2、如果添加的元素e与tmp相等,则表示元素存在,直接返回失败 157 if (comp == 0) { 158 return false; 159 }//3、如果添加的元素e小于tmp节点,则要添加到tmp的左子树中的某个位置上 160 else if (comp < 0) { 161 //如果tmp的左子树为不为空,则还要继续找添加点 162 if (tmp.left != null) { 163 tmp = tmp.left; 164 }//如果tmp没有左节点,则或把新增元素设置成tmp的左子节点 165 else { 166 tmp.left = new Entry<E>(e, tmp); 167 size++; 168 return true; 169 } 170 }//4、否则在tmp的右子树中找添加位置 171 else { 172 //如果tmp的右子树为不为空,则还要继续找添加点 173 if (tmp.right != null) { 174 tmp = tmp.right; 175 }//如果tmp没有右子节点,则或把新增元素设置成tmp的右子节点 176 else { 177 tmp.right = new Entry<E>(e, tmp); 178 size++; 179 return true; 180 } 181 } 182 } 183 } 184 } 185 186 /** 187 * 删除指定的数据域的元素 188 * @param p 189 * @return boolean 190 */ 191 public boolean remove(E p) { 192 //根据数据域查找待删除的元素 193 Entry<E> tmp = getEntry(p); 194 if (tmp == null) {//如果元素没有找到,则删除失败 195 return false; 196 } else { 197 //删除元素 198 deleteEntry(tmp); 199 return true; 200 } 201 } 202 203 /** 204 * 删除指定的节点实现 205 * 206 * 算法思想: 207 * 208 * 1、若待删除的节点p是叶子节点,则直接删除该节点; 209 * 210 * 2、若待删除的节点p只有一个子节点,则将p的子节点与p的父节点直接连接,然后删除节点p; 211 * 为什么只有一个子节点时可以直接接到删除节点的父节点下面呢?因为只有一个子节点,直接接上 212 * 去不会影响排序子节点本身的排序,当然更不会影响另外一个子树(因为另一子树跟本不存在!); 213 * 214 * 3、若待删除节点p有两个子节点时,应该使用中序遍历方式得到的直接前置节点S或直接后继节点s 215 * 的值来代替点s的值,然后删除节点s,(注:节点s肯定属于上述1、2情况之一)而不是直接删除 216 * p,这样可以将该删除问题转换成上面1、2问题; 217 * 218 * @param p 指向被删除的节点p 219 */ 220 private void deleteEntry(Entry<E> p) { 221 //如果删除的节点p有左右子树时,将问题转换成删除叶子节点或只有一个子节点的节点问题 222 if (p.left != null && p.right != null) { 223 /* 224 * 删除有两个子节点的节点示例图(转换成删除只有一个子节点的节点或叶子节点问题): 225 * 226 * p → 80 将直接后继元素s的elem替换p元素的 90 227 * /\ elem,然后将p指向s,这样将问题转换 /\ 228 * 15 110 成删除只有一个子节点的节点p问题了 15 110 229 * / / 230 * s → 90 → s、p → 90 231 * \ \ 232 * 105 105 233 * 234 */ 235 /* 236 * 查找待删除节点p的中序遍历节点的直接后继节点。注,该后继节点只可能是叶子节点 237 * (该叶子节点只可能是以下两种:一种是就是右子节点本身,因为可能待删除节点p的 238 * 右子节点没有左右子树了;第一种就是待删除节点p右子节点的左子树上最左边的叶子 239 * 节点),或只有一个右子节点的节点(该节点只可能是在上述第二种叶子节点上上多了 240 * 一个右子树罢了) 241 */ 242 Entry<E> s = successor(p); 243 /* 244 * 当待删除的节点p的左右子树都存在时,我们不正真真删除p这个节点,而是用后继节点s 245 * 来替换p节点,具体作法就是什么后继节点的数据域elem替换待删除节点p的数据域elem, 246 * 替换之后,我们让真真待删除的节点p变成后继节点s(即让p指向s),因为这样将问题转 247 * 换成删除叶子节点或只有一个子节点(这个子节点有个特点就是左子树一定为空)的节点的 248 * 问题了,这样也就能共用下面真正的删除叶子节点与删除只有一个子节点的节点边辑了 249 */ 250 p.elem = s.elem;//使用后继节点s的数据替换p的数据域 251 252 p = s;//让p指向直接后继节点s,这样删除p时实质上是删除的直接后节点 253 } 254 255 /* 256 * !! 注,程序运行到这里时,如果待删除的节点p左右子树都存在时,已被上面程序逻辑转换成了 257 * 删除叶子节点或只有一个子节点(一定是右子节点)的节点问题了,当然下面的删除逻辑还不只适 258 * 用于删除只有一个右子节点的节点,还适用于删除只有一个左子节点的节点,总之能适用于删除只 259 * 一个子节点的节点,而不管这个子节点是左还是有。 260 * 261 * 下面程序开始删除叶子节点或只有一个子节点的节点: 262 */ 263 264 //若待删除的节点p是叶子节点,则直接删除该节点,无需用后继节点填补 265 if (p.left == null && p.right == null) { 266 /* 267 * 删除叶子节点示例图: 268 * 269 * 80 80 270 * /\ /\ 271 * 20 110 → 20 110 272 * \ / / 273 * p → 50 90 90 274 * \ \ 275 * 105 105 276 * 277 * p指向要删除的节点50,要做的就是将50的父节点Entry对象(元素为20的Entry对象)的 278 * 右子节点修改为null 279 */ 280 //若待删除的节点p是根元素,且又没有子节点时,直接删除释放节点 281 if (p.paraent == null) { 282 root = null; 283 }//如果被删除的节点p为左叶子节点,则把父节点的左指针置为null 284 else if (p == p.paraent.left) { 285 p.paraent.left = null; 286 }//否则被删除的节点p为右叶子节点,则把父节点的右指针置为null 287 else { 288 p.paraent.right = null; 289 } 290 291 }//否则删除的是只有一个子节点(不管左还是右都可)的节点时,则需使用后继节点填补 292 else { 293 294 /* 295 * !! 注,到此,p只有一个子节点,不可能即有左子节点同时又有右子节点,因为如果有, 296 * 前面逻辑也会把p转换成了只具有一个右子节点的节点 297 */ 298 299 /* 300 * 删除只有一个子节点的元素示例图: 301 * 302 * 80 80 303 * /\ /\ 304 * p → 20 110 → 50 110 305 * \ / / 306 * rep → 50 90 90 307 * \ \ 308 * 105 105 309 * 310 * p指向要删除的节点20。不能在二叉搜索树中留下空洞,所以必须要用某个元素来取代20, 311 * 那么选择哪个元素好?逻辑上选择被删除的子节点15。因此需把15连到20的父节点上。 312 */ 313 Entry<E> rep;// 指向用来替换被删除节点p的,只可能是左子节点或是右子节点 314 315 if (p.left != null) { 316 //如果只有左子节点时,则用左子节点替换要删除的节点p 317 rep = p.left; 318 } else {//否则只有右子树,则用右子节点替换要删除的节点p 319 rep = p.right; 320 } 321 //--修改替换节点的父指针指向 322 /* 323 * 使替换节点的父指针指向删除节点p的父节点,注,如果删除的是根节点,则左右子节点 324 * 的父指针都会指向null,则此时需将root指向左或右子节点,即左或右子节点将成为根 325 * 节点 326 */ 327 rep.paraent = p.paraent;//设置替换元素的父 328 329 //--修改被删除元素p的父节点的左或右指针指向 330 //如果删除的是根元素,则重置root 331 if (p.paraent == null) { 332 root = rep; 333 }//如果删除的是某节点的左子节点 334 else if (p == p.paraent.left) { 335 p.paraent.left = rep; 336 }//否则删除的是某节点的右子节点 337 else { 338 p.paraent.right = rep; 339 } 340 341 } 342 //让删除节点成为孤立点 343 p.paraent = null; 344 p.left = null; 345 p.right = null; 346 347 size--;//删除节点后树节点个数减一 348 } 349 350 /** 351 * 根据指定的数据域查找元素 352 * @param e 353 * @return Entry<E> 354 */ 355 private Entry<E> getEntry(E e) { 356 Entry<E> tmp = root; 357 int c; 358 while (tmp != null) {//如果树不为空 359 c = e.compareTo(tmp.elem); 360 //如果与tmp元素相等,则返回 361 if (c == 0) { 362 return tmp; 363 }//如果比tmp小,则在tmp的左子树中找 364 else if (c < 0) { 365 tmp = tmp.left; 366 }//如果比tmp大,则在tmp的右子树中找 367 else { 368 tmp = tmp.right; 369 } 370 } 371 //树本身就为空或树不为空时没有找到时 372 return null; 373 } 374 375 /** 376 * 查找指定节点的中序遍历序列的直接后继节点 377 * 378 * 注,无需在左子树上找,因为中序遍历时,左子树上的节点都会在该节点的前面遍历。 379 * 380 * 1、如果待查找的节点有右子树,则后继节点一定在右子树上,此时右子树上的某个节点可能成为后 381 * 继节点:一是如果待查节点的右子树没有左子树(有没有右子树无所谓)时,直接就返回该待查节点 382 * 的右子节点;二是如果待点节点的右子节点有左子树,则查找右子节点的最左边的左子树节点(注, 383 * 该节点一点是左叶子节点或只有一个右子节点的左节点,查找过程要一直向左,即遍历时只向左拐, 384 * 不可向右) 385 * 386 * 2、如果待查找的节点没有右子树,则需要从该节点向根的方向遍历(不可向左或右拐),后继节点只 387 * 可能在祖宗节点中产生(包括父节点与根节点在内),此情况分两种:一种就是待查节点为某节点的左 388 * 子树,则此时的后继为父节点;第二种就是当待查节点为某个节点的右子树时,则需沿根的方向向上找, 389 * 一直找到第一个有左子树的祖宗节点即为后继节点,或到根为止还没有找到(则该节点只可能为中序遍 390 * 历的最后节点)。 391 * 392 * @param e 需要查找哪个节点的直接后继节点 393 * @return Entry<E> 直接后继节点 394 */ 395 private Entry<E> successor(Entry<E> e) { 396 if (e == null) { 397 return null; 398 }//如果待找的节点有右子树,则在右子树上查找 399 else if (e.right != null) { 400 /* 401 * 查找50节点的直接后继,查找结果为55 402 * 50 403 * \ 404 * 75 405 * / 406 * 61 407 * /\ 408 * 55 68 409 * \ 410 * 59 411 */ 412 //默认后继节点为右子节点(如果右子节点没有左子树时即为该节点) 413 Entry<E> p = e.right; 414 while (p.left != null) { 415 //注,如果右子节点的左子树不为空,则在左子树中查找,且后面找时要一直向左拐 416 p = p.left; 417 } 418 return p; 419 }//如果待查节点没有右子树,则要在祖宗节点中查找后继节点 420 else { 421 422 /* 423 * 没有右子树的节点且为父节点的右子节点36的直接后继为37,同样节点68的直接后继为75 424 * 没有右子树的节点且为父节点的左子节点37的直接后继为50,同样节点28的直接后继为30 425 * 75为最后节点,所以直接后继为null 426 * 427 * 50 428 * /\ 429 * 37 75 430 * / / 431 * 25 61 432 * /\ /\ 433 * 15 30 55 68 434 * /\ \ 435 * 28 32 59 436 * \ 437 * 36 438 * / 439 * 35 440 */ 441 //默认后继节点为父节点(如果待查节点为父节点的左子树,则后继为父节点) 442 Entry<E> p = e.paraent; 443 Entry<E> c = e;//当前节点,如果其父不为后继,则下次指向父节点 444 //如果待查节点为父节点的右节点时,继续向上找,一直要找到c为左子节点,则p才是后继 445 while (p != null && c == p.right) { 446 c = p; 447 p = p.paraent; 448 } 449 return p; 450 } 451 } 452 453 /** 454 * 查找指定节点的中序遍历序列的直接前驱节点 455 * 456 * 查找逻辑与找直接后继节点刚好相反或对称 457 * @param e 458 * @return 459 */ 460 private Entry<E> precursor(Entry<E> e) { 461 if (e == null) { 462 return null; 463 }//如果待找的节点有左子树,则在在子树上查找 464 else if (e.left != null) { 465 //默认直接前驱节点为左子节点(如果左子节点没有右子树时即为该节点) 466 Entry<E> p = e.left; 467 while (p.right != null) { 468 //注,如果左子节点的右子树不为空,则在右子树中查找,且后面找时要一直向右拐 469 p = p.right; 470 } 471 return p; 472 }//如果待查节点没有左子树,则要在祖宗节点中查找前驱节点 473 else { 474 //默认前驱节点为父节点(如果待查节点为父节点的右子树,则前驱为父节点) 475 Entry<E> p = e.paraent; 476 Entry<E> current = e;//当前节点,如果其父不为前驱,则下次指向父节点 477 //如果待查节点为父节点的左节点时,继续向上找,一直要找到current为p的右子节点,则s才是前驱 478 while (p != null && current == p.left) { 479 current = p; 480 p = p.paraent; 481 } 482 return p; 483 } 484 } 485 486 /** 487 * 提供迭代器接口 488 * @return 489 */ 490 public Iterator<E> iterator() { 491 return new TreeItrator(); 492 } 493 494 /** 495 * 树的迭代器 496 * @author jzj 497 * @date 2009-12-19 498 */ 499 500 public class TreeItrator implements Iterator<E> { 501 502 private Entry<E> lastRet;//最近一次next操作返回的节点 503 private Entry<E> next;//下一个节点 504 private Entry<E> endNode;//树最后一个节点 505 506 TreeItrator() { 507 //初始化时,让next指根节点,如果根没有左子树时,则就为根 508 next = root; 509 if (next != null) { 510 //如果next还有左子树时,则为左子节点,直到最左边节点为止 511 while (next.left != null) { 512 next = next.left;//从根节点开始,一直向左拐 513 } 514 } 515 } 516 517 //是否还有下一个节点 518 public boolean hasNext() { 519 return next != null; 520 } 521 522 //返回下一个节点,即next指向的节点 523 public E next() { 524 if (next == null) { 525 throw new NoSuchElementException(); 526 } 527 lastRet = next; 528 next = successor(next);//下一个为直接后继节点 529 530 //如果后继节点为null,表示该next指向的节点为树中的最后节点 531 if (next == null) { 532 /* 533 * 使用endNode记录下最末节点,以便 previous 使用,因为next最终会指向null, 534 * 即好比指向了最末节点的后面,此时previous是要返回最末节点的,所以需要标记 535 * 与存储起来 536 */ 537 endNode = lastRet; 538 } 539 return lastRet.elem; 540 } 541 542 //是否有前驱节点 543 public boolean hasPrevious() { 544 return (next != null && precursor(next) != null) || endNode != null; 545 } 546 547 //返回前驱节点 548 public E previous() { 549 if ((next != null && precursor(next) == null)) { 550 throw new NoSuchElementException(); 551 } 552 553 //如果已迭代到了最末节点 554 if (endNode != null) { 555 //使用lastReturned与next都指向最末节点 556 lastRet = next = endNode; 557 endNode = null; 558 } else {//如果lastReturned指向的不是最末节点时 559 lastRet = next = precursor(next); 560 } 561 562 return lastRet.elem; 563 } 564 565 //删除最后一次next或previous方法返回的节点 566 public void remove() { 567 if (lastRet == null) { 568 throw new IllegalStateException(); 569 } 570 571 /* 572 * 注,如果删除的节点(lastRet指向的节点)具有左右子节点,则在调用 573 * deleteEntry方法删除后,它会使用这个后继节点的数据域替换待删除的节点的数据 574 * 域,next指向的节点会被移到lastRet位置,所以如果此时不使用next回退 575 * 到lastRet的位置,则 next指向的节点(Entry)对象将是一个不在这棵树中 576 * 的节点。如果删除的是一个叶子节点或只有一个节点的节点时不会有这种问题。 577 * 578 * 删除有两个子节点的节点40,删除后next指向的节点已被移到lastRet,所以 579 * next需后退 580 * 581 * 先后退 后删除50 582 * lastRet → 40 next、lastRet → 50 next → 50 583 * /\ → /\ /\ 584 * 20 75 20 75 → 20 75 585 * /\ /\ \ 586 * next → 50 80 50 80 50 80 587 * 588 * 删除只有一个子节点的节点20,删除后next指向不需要改变,因为next指向的元素没有 589 * 发生变化,删除前后还是指向原来的30 590 * 50 50 591 * /\ /\ 592 * lastRet → 20 75 → next → 30 75 593 * \ / 594 * next → 30 28 595 * / 596 * 28 20 597 */ 598 if (lastRet.left != null && lastRet.right != null) { 599 next = lastRet; 600 } 601 602 deleteEntry(lastRet);//删除最后一次next方法返回的元素 603 604 lastRet = null;//不能连续删除,只有在使用next后才能删除 605 } 606 } 607 608 private static void fixText() { 609 BinSearchTree<Integer> bst = new BinSearchTree<Integer>(); 610 bst.add(50); 611 bst.add(37); 612 bst.add(75); 613 bst.add(25); 614 bst.add(61); 615 bst.add(15); 616 bst.add(30); 617 bst.add(55); 618 bst.add(68); 619 bst.add(28); 620 bst.add(32); 621 bst.add(59); 622 bst.add(36); 623 bst.add(36);//添加一个重复,但不能添加进去 624 625 //是否包含 626 System.out.println(bst.contanins(36));//true 627 System.out.println(bst.contanins(38));//false 628 629 //大小 630 System.out.println(bst.size());//13 631 632 //遍历 633 Iterator<Integer> itr = bst.iterator(); 634 while (itr.hasNext()) { 635 //15 25 28 30 32 36 37 50 55 59 61 68 75 636 System.out.print(itr.next() + " "); 637 } 638 System.out.println(); 639 640 //从后往前遍历 641 BinSearchTree<Integer>.TreeItrator titr = (BinSearchTree<Integer>.TreeItrator) itr; 642 while (titr.hasPrevious()) { 643 //75 68 61 59 55 50 37 36 32 30 28 25 15 644 System.out.print(titr.previous() + " "); 645 } 646 System.out.println(); 647 648 //测试迭代器的 previous 649 titr = (BinSearchTree<Integer>.TreeItrator) bst.iterator(); 650 System.out.println(titr.hasPrevious());//false 651 System.out.println(titr.next());//15 652 System.out.println(titr.previous());//15 653 System.out.println(titr.next());//15 654 System.out.println(titr.next());//25 655 System.out.println(titr.next());//28 656 System.out.println(titr.previous());//28 657 658 //删除根叶子节点36 659 bst.remove(36); 660 System.out.println(bst.size());//12 661 itr = bst.iterator(); 662 while (itr.hasNext()) { 663 //15 25 28 30 32 37 50 55 59 61 68 75 664 System.out.print(itr.next() + " "); 665 } 666 System.out.println(); 667 668 //删除只有一个左子节点的节点37 669 bst.remove(37); 670 System.out.println(bst.size());//11 671 itr = bst.iterator(); 672 while (itr.hasNext()) { 673 //15 25 28 30 32 50 55 59 61 68 75 674 System.out.print(itr.next() + " "); 675 } 676 System.out.println(); 677 678 //删除只有一个右子节点的节点55 679 bst.remove(55); 680 System.out.println(bst.size());//10 681 itr = bst.iterator(); 682 while (itr.hasNext()) { 683 //15 25 28 30 32 50 59 61 68 75 684 System.out.print(itr.next() + " "); 685 } 686 System.out.println(); 687 688 //删除有左右子节点的根节点50 689 bst.remove(50); 690 System.out.println(bst.size());//9 691 itr = bst.iterator(); 692 while (itr.hasNext()) { 693 //15 25 28 30 32 59 61 68 75 694 System.out.print(itr.next() + " "); 695 } 696 System.out.println(); 697 698 //下面通过迭代器删除节点根节点59 699 itr = bst.iterator(); 700 while (itr.hasNext()) { 701 if (itr.next() == 59) { 702 itr.remove();//删除最近一次next返回的节点 703 break; 704 } 705 } 706 707 while (itr.hasNext()) { 708 //61 68 75 709 System.out.print(itr.next() + " "); 710 itr.remove(); 711 } 712 713 System.out.println(); 714 System.out.println(bst.size());//5 715 } 716 717 private static void randomTest() { 718 BinSearchTree<Integer> myTree = new BinSearchTree<Integer>(); 719 Random random = new Random(); 720 System.out.print("随机产生的节点为:"); 721 int num = 0; 722 //直到树的节点数为n止 723 while (myTree.size() < 21) { 724 num = random.nextInt(100); 725 myTree.add(num); 726 System.out.print(num + " "); 727 } 728 System.out.println(""); 729 730 System.out.println("这棵平衡二叉树的总节点数:" + myTree.size()); 731 System.out.println("在树中查找 " + num + " 节点:" + myTree.contains(new Integer(num))); 732 System.out.println("在树中查找 100 节点:" + myTree.contains(new Integer(100))); 733 System.out.print("中序遍历(从前往后):"); 734 BinSearchTree<Integer>.TreeItrator itr = (BinSearchTree<Integer>.TreeItrator) myTree 735 .iterator(); 736 while (itr.hasNext()) { 737 System.out.print(itr.next() + " "); 738 } 739 System.out.println(""); 740 741 System.out.print("中序遍历(从后往前):"); 742 while (itr.hasPrevious()) { 743 System.out.print(itr.previous() + " "); 744 } 745 System.out.println(""); 746 747 myTree.remove(num); 748 System.out.print("删除节点 " + num + " 后遍历:"); 749 itr = (BinSearchTree<Integer>.TreeItrator) myTree.iterator(); 750 while (itr.hasNext()) { 751 System.out.print(itr.next() + " "); 752 } 753 System.out.println(""); 754 755 System.out.println("使用迭代器删除所有节点"); 756 itr = (BinSearchTree<Integer>.TreeItrator) myTree.iterator(); 757 while (itr.hasNext()) { 758 itr.next(); 759 itr.remove(); 760 } 761 System.out.println("删除后树的总节点数:" + myTree.size()); 762 763 } 764 765 private static void order() { 766 BinSearchTree<Integer> myTree = new BinSearchTree<Integer>(); 767 Random random = new Random(); 768 int num = 0; 769 while (myTree.size() < 10) { 770 num = random.nextInt(100); 771 myTree.add(num); 772 } 773 774 System.out.print("前序遍历 - "); 775 myTree.preOrder(new Visitor<Integer>() { 776 777 public void visit(Integer e) { 778 System.out.print(e + " "); 779 780 } 781 }); 782 System.out.println(); 783 784 System.out.print("中序遍历 - "); 785 myTree.inOrder(new Visitor<Integer>() { 786 787 public void visit(Integer e) { 788 System.out.print(e + " "); 789 790 } 791 }); 792 System.out.println(); 793 794 System.out.print("后序遍历 - "); 795 myTree.postOrder(new Visitor<Integer>() { 796 797 public void visit(Integer e) { 798 System.out.print(e + " "); 799 800 } 801 }); 802 System.out.println(); 803 804 System.out.print("层次遍历 - "); 805 myTree.levelOrder(new Visitor<Integer>() { 806 807 public void visit(Integer e) { 808 System.out.print(e + " "); 809 810 } 811 }); 812 System.out.println(); 813 } 814 815 //测试 816 public static void main(String[] args) { 817 fixText();//固定测试 818 randomTest();//随机测试 819 order();//遍历 820 } 821 }