[数据结构与算法]二叉排序(搜索)树实现

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289804.html

定义

二叉排序树又称二叉查找树或二叉搜索树,它或者是一棵空树,或者是具有如下性质的二叉树:
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 }
时间: 2024-11-05 18:48:29

[数据结构与算法]二叉排序(搜索)树实现的相关文章

9-7-平衡二叉排序(搜索)树-查找-第9章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第9章  查找 - 平衡二叉排序(搜索)树 ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Base.c        相关测试数据下载  链接? 数据包    

数据结构(十二)——排序算法

数据结构(十二)--排序算法 一.排序简介 1.排序的一般定义 排序是计算机中经常进行的操作,目的在于将一组无序的数据元素调整为有序的数据元素.序列:1,20,45,5,2,12排序后:1,2,5,12,20,45 2.排序的数学定义 3.排序的稳定性 如果序列中的两个元素R[i].R[j],关键字分别为K[i].K[j],并且在排序之前R[i]排在R[j]前面,如果排序操作后,元素R[i]仍然排在R[j]前面,则排序方法是稳定的:否则排序是不稳定的. 4.排序实现的关键 比较:任意两个数据元素

数据结构与算法二

1.课程安排表: 1. 线性表 2. 字符串 3. 栈和队列 4.树 5.查找 6.排序 7.暴力枚举法 8.广度优先搜索 9.深度优先搜索 10.分治 11.贪心 12.动态规划 13.图 14.数学方法与常见模型 15.大整数运算 16. 基础功能 2.   编程技巧: 1.把较大的数组放在main 函数(全局变量)外,作为全局变量,这样可以防止栈溢出,因为栈的大小是有限制的.GCC (C编译器) 段错误 2.如果能够预估栈,队列的上限,则不要用stack,queue,使用数组来模拟,这样速

数据结构之(二叉)堆

(二叉)堆是一个数组,是一颗近似完全二叉树,分为大顶堆&小顶堆.表示堆的数组A有两个属性:(1)A.length表示数组元素的个数:(2)A.heap-size表示有多少个堆元素存储在数组A中.更多的关于堆的性质的介绍:算法导论第三版:p85-p89.编程珠玑:p141-p145. 堆的操作主要包括堆插入.堆删除两个,而堆插入设计到SiftUp操作(自底向上调整),堆删除涉及到SiftDown操作(自顶向下调整,大顶堆时对应算法导论上的MAX-HEAPIFY操作). 下面是大顶堆和小顶堆的基本操

白话经典算法二叉堆排序之思想简介

常用的排序算法有冒泡排序,插入排序和选择排序.他们的时间复杂度是o(n2),与数据量的平方成正比.他们的效率还是比较低的.现在来说说他们的效率为什么比较低下.以冒泡排序为例,它每一轮都是与相邻的元素进行交换,交换的距离为1,每次每个(没有冒泡出来的)元素都要与前一个比较再交换.每次相邻的比较只能比较出两个元素的大小,不能以整个数组进行参照来确定在整个数组里的大小,也就是说每次的比较不能确定其他元素的相对位置,因而每次比较的贡献不大,所以这样的比较是笨拙的,进而需要完全比较O(n2)次才能得出正确

【数据结构与算法】选择排序

选择排序没什么好说的,直接上代码吧 public class SelectSort { public void selectSort(int[] in) { int inLength = in.length; int minIndex = 0; for (int i = 0; i < inLength; i++) { minIndex = i; for (int j = i + 1; j < inLength; j++) { if (in[j] < in[minIndex]) { min

二叉排序

二叉排序,附带插入,查找和删除值.. /* Author: buer Date: 2017/9/18 11:56:02 */ #include <stdio.h> #include <stdlib.h> typedef struct Tree { int data; struct Tree *lchild; struct Tree *rchild; }Tree; void createBiTree(Tree *root); void insertData(Tree *root, i

Java数据结构和算法(二)——数组

数组的用处是什么呢?--当你需要将30个数进行大小排列的时候,用数组这样的数据结构存储是个很好的选择,当你是一个班的班主任的时候,每次要记录那些学生的缺勤次数的时候,数组也是很有用.数组可以进行插入,删除,查找等. 1)创建和内存分配 Java中有两种数据类型,基本类型和对象类型,也有人称为引用类型,Java中把数组当成对象,创建数组时使用new操作符. int array[] = new int[10]; 既然是对象,那么array便是数组的一个引用,根据Java编程思想(一) -- 一切都是

【数据结构与算法】希尔排序

希尔排序的时间复杂度是O(n^1.3)~O(n^2),空间复杂度是O(1). 代码如下: /** * 源码名称: ShellSort.java * 日期:2014-08-11 * 程序功能:希尔排序 * 版权:[email protected] * 作者:A2BGeek */ public class ShellSort { public void shellSort(int[] in) { int length = in.length; int span = length / 2; int i