二分法猜数字的游戏应该每个人都知道,通过对猜测数字“大了”、“小了”的情况判断,来猜出最终的数字。序列范围为 n 的集合,复杂度为 O(log_2 n),即最多需要 log_2 n 次可以猜到最终数字。

引子

二分法的查找过程是,在一个有序的序列中,每次都会选择有效范围中间位置的元素作判断,即每次判断后,都可以排除近一半的元素,直到查找到目标元素或返回不存在,所以 n 个有序元素构成的序列,查找的时间复杂度为 O(log_2 n)。既然线性结构能够做到查询复杂度为 O(log_2 n) 级别,那二叉搜索树产生又有何必要呢?毕竟二叉搜索树的查询复杂度只是介于 O(log_2 n)~O(n) 之间,并不存在查询优势。

定义

二叉搜索树是一种节点值之间具有一定数量级次序的二叉树,对于树中每个节点:

  • 若其左子树存在,则其左子树中每个节点的值都不大于该节点值;
  • 若其右子树存在,则其右子树中每个节点的值都不小于该节点值。

示例:


 
BST

查询复杂度

观察二叉搜索树结构可知,查询每个节点需要的比较次数为节点深度加一。如深度为 0,节点值为 “6” 的根节点,只需要一次比较即可;深度为 1,节点值为 “3” 的节点,只需要两次比较。即二叉树节点个数确定的情况下,整颗树的高度越低,节点的查询复杂度越低。

二叉搜索树的两种极端情况:

【1】 完全二叉树,所有节点尽量填满树的每一层,上一层填满后还有剩余节点的话,则由左向右尽量填满下一层。如上图BST所示,即为一颗完全二叉树;
【2】每一层只有一个节点的二叉树。如下图SP_BST所示:

 
SP_BST

第【1】种情况下的查找次数分析:由上一章 二叉树 可知,完美二叉树中树的深度与节点个数的关系为:n=2^{d+1}-1。设深度为 d 的完全二叉树节点总数为 n_c,因为完全二叉树中深度为 d 的叶子节点层不一定填满,所以有 n_c \le 2^{d+1}-1,即:d+1 \ge log_2{(n_c+1)},因为 d+1 为查找次数,所以完全二叉树中查找次数为:\lceil log_2{(n_c+1)} \rceil

第【2】种情况下,树中每层只有一个节点,该状态的树结构更倾向于一种线性结构,节点的查询类似于数组的遍历,查询复杂度为 O(n)

所以二叉搜索树的查询复杂度为 O(log_2 n)~O(n)

构造复杂度

二叉搜索树的构造过程,也就是将节点不断插入到树中适当位置的过程。该操作过程,与查询节点元素的操作基本相同,不同之处在于:

  • 查询节点过程是,比较元素值是否相等,相等则返回,不相等则判断大小情况,迭代查询左、右子树,直到找到相等的元素,或子节点为空,返回节点不存在
  • 插入节点的过程是,比较元素值是否相等,相等则返回,表示已存在,不相等则判断大小情况,迭代查询左、右子树,直到找到相等的元素,或子节点为空,则将节点插入该空节点位置。

由此可知,单个节点的构造复杂度和查询复杂度相同,为 O(log_2 n)~O(n)

删除复杂度

二叉搜索树的节点删除包括两个过程,查找和删除。查询的过程和查询复杂度已知,这里说明一下删除节点的过程。

节点的删除有以下三种情况:
  1. 待删除节点度为零;
  2. 待删除节点度为一;
  3. 待删除节点度为二。

第一种情况如下图 s_1 所示,待删除节点值为 “6”,该节点无子树,删除后并不影响二叉搜索树的结构特性,可以直接删除。即二叉搜索树中待删除节点度为零时,该节点为叶子节点,可以直接删除;

 
s_1
 
s_1'

第二种情况如下图 s_2 所示,待删除节点值为 “7”,该节点有一个左子树,删除节点后,为了维持二叉搜索树结构特性,需要将左子树“上移”到删除的节点位置上。即二叉搜索树中待删除的节点度为一时,可以将待删除节点的左子树或右子树“上移”到删除节点位置上,以此来满足二叉搜索树的结构特性。

 
s_2
 
s_2'

第三种情况如下图 s_3 所示,待删除节点值为 “9”,该节点既有左子树,也有右子树,删除节点后,为了维持二叉搜索树的结构特性,需要从其左子树中选出一个最大值的节点,“上移”到删除的节点位置上。即二叉搜索树中待删除节点的度为二时,可以将待删除节点的左子树中的最大值节点“移动”到删除节点位置上,以此来满足二叉搜索树的结构特性。

其实在真实的实现代码中,该情况下的实际节点删除操作是:
1.查找出左子树中的最大值节点 Node_{max}
2.替换待删除节点 node 的值为 Node_{max} 的值
3.删除 Node_{max} 节点
因为 Node_{max} 作为左子树的最大值节点,所以节点的度一定是 0 或 1,所以删除节点的情况就转移为以上两种情况。

 
s_3
 
s_3'

之前提到二叉搜索树中节点的删除操作,包括查询和删除两个过程,这里称删除节点后,维持二叉搜索树结构特性的操作为“稳定结构”操作,观察以上三种情况可知:

  • 前两种情况下,删除节点后,“稳定结构”操作的复杂度都是常数级别,即整个的节点删除操作复杂度为 O(log_2 n)~O(n)
  • 第三种情况下,设删除的节点为 p,“稳定结构”操作需要查找 p 节点左子树中的最大值,也就是左子树中最“右”的叶子结点,即“稳定结构”操作其实也是一种内部的查询操作,所以整个的节点删除操作其实就是两个层次的查询操作,复杂度同为 O(log_2 n)~O(n)

性能分析

由以上查询复杂度、构造复杂度和删除复杂度的分析可知,三种操作的时间复杂度皆为 O(log_2 n)~O(n)。下面分析线性结构的三种操作复杂度,以二分法为例:

  • 查询复杂度,时间复杂度为 O(log_2 n),优于二叉搜索树;
  • 元素的插入操作包括两个步骤,查询和插入。查询的复杂度已知,插入后调整元素位置的复杂度为 O(n),即单个元素的构造复杂度为:O(n)
  • 删除操作也包括两个步骤,查询和删除,查询的复杂度已知,删除后调整元素位置的复杂度为 O(n),即单个元素的删除复杂度为:O(n)

由此可知,二叉搜索树相对于线性结构,在构造复杂度和删除复杂度方面占优;在查询复杂度方面,二叉搜索树可能存在类似于斜树,每层上只有一个节点的情况,该情况下查询复杂度不占优势。



1.完整实现

  1 package DataStructure.Tree;
  2 
  3 import DataStructure.Tree.printer.BinaryTreeInfo;
  4 import DataStructure.main1.AbstractList;
  5 
  6 import java.util.Comparator;
  7 import java.util.LinkedList;
  8 import java.util.Queue;
  9 
 10 public class BinarySearchTree <E > implements BinaryTreeInfo {
 11     private int size;
 12     private Node<E> root;
 13     private Comparator<E> comparator;
 14     public BinarySearchTree(Comparator<E> comparator){
 15         this.comparator=comparator;
 16     }
 17     public BinarySearchTree(){
 18         this(null);
 19     }
 20     public int size(){
 21         return size;
 22     }
 23     public boolean isEmpty(){
 24         return size==0;
 25     }
 26 
 27     public void clear() {
 28     root=null;
 29     size=0;
 30     }
 31     public static abstract class Visitor<E> {
 32         boolean stop;
 33         /**
 34          * @return 如果返回true,就代表停止遍历
 35          */
 36         public abstract boolean visit(E element);
 37     }
 38     public void add(E element) {
 39         elementnotnullcheck(element);
 40         //添加第一个节点
 41         if(root==null){
 42             root =new Node<>(element,null);
 43             size++;
 44             return;
 45         }
 46         //添加的不是第一个节点,找到父节点
 47         Node<E> node =root;
 48         Node<E> parent =root;//每次需要保存上一次的父节点
 49         int cmp=0;
 50         while (node!=null) {
 51              cmp = compare(element, node.element);
 52             parent=node;
 53             if (cmp > 0) {
 54                 node = node.right;
 55             } else if (cmp < 0) {
 56                 node = node.left;
 57             } else {
 58                 node.element=element;//覆盖相等的,意义是
 59                 return;
 60             }
 61         }
 62         //看看插入到节点的哪个方向
 63         Node<E> newnode=new Node<>(element,parent);
 64         if(cmp>0){
 65             parent.right=newnode;
 66         }
 67         else if(cmp<0) {
 68             parent.left=newnode;
 69         }
 70         size++;
 71 
 72     }
 73 
 74 
 75 
 76 
 77     public boolean contains(E element) {
 78         return node(element)!=null;
 79     }
 80 
 81     //element不能为null
 82     private void elementnotnullcheck(E element){
 83         if(element==null){
 84             throw  new IllegalArgumentException("element must not be null");
 85         }
 86     }
 87     //前序遍历
 88     public void preorder(Visitor<E> visitor) {
 89         if (visitor == null) return;
 90         preorder(root, visitor);
 91     }
 92 
 93     private void preorder(Node<E> node, Visitor<E> visitor) {
 94         if (node == null || visitor.stop) return;
 95 
 96         visitor.stop = visitor.visit(node.element);
 97         preorder(node.left, visitor);
 98         preorder(node.right, visitor);
 99     }
100 
101     public void inorder(Visitor<E> visitor) {
102         if (visitor == null) return;
103         inorder(root, visitor);
104     }
105 
106     private void inorder(Node<E> node, Visitor<E> visitor) {
107         if (node == null || visitor.stop) return;
108 
109         inorder(node.left, visitor);
110         if (visitor.stop) return;
111         visitor.stop = visitor.visit(node.element);
112         inorder(node.right, visitor);
113     }
114 
115     public void postorder(Visitor<E> visitor) {
116         if (visitor == null) return;
117         postorder(root, visitor);
118     }
119 
120     private void postorder(Node<E> node, Visitor<E> visitor) {
121         if (node == null || visitor.stop) return;
122 
123         postorder(node.left, visitor);
124         postorder(node.right, visitor);
125         if (visitor.stop) return;
126         visitor.stop = visitor.visit(node.element);
127     }
128 
129     /**
130      * 层序遍历,首先,根节点入队,然后循环以下步骤 ,直到队列为空1.将队头节点A出队,进行访问2.将A的左节点入队,3.将A的右节点入队
131      * @param visitor
132      */
133     public void levelOrder(Visitor<E> visitor) {
134         if (root == null || visitor == null) return;
135 
136         Queue<Node<E>> queue = new LinkedList<>();
137         queue.offer(root);
138 
139         while (!queue.isEmpty()) {
140             Node<E> node = queue.poll();
141             if (visitor.visit(node.element)) return;
142 
143             if (node.left != null) {
144                 queue.offer(node.left);
145             }
146 
147             if (node.right != null) {
148                 queue.offer(node.right);
149             }
150         }
151     }
152     /**
153      * 前驱节点: 中序遍历时的前一个节点
154      * 求前驱节点
155      */
156     protected Node<E> predecessor(Node<E> node) {
157         if (node == null) return null;
158 
159         // 前驱节点在左子树中(left.right.right.right....)
160         Node<E> p = node.left;
161         if (p != null) {
162             // 左子树不为空,则找到它的最右节点
163             while (p.right != null) {
164                 p = p.right;
165             }
166             return p;
167         }
168 
169         // 能来到这里说明左子树为空, 则从父节点、祖父节点中寻找前驱节点
170         // 当父节点不为空, 且某节点为父节点的左子节点
171         // 则顺着父节点找, 直到找到【某结点为父节点或祖父节点的右子树中】时
172         while (node.parent != null && node.parent.left == node) {
173             node = node.parent;
174         }
175 
176         // 来到这里有以下两种情况:
177         // node.parent == null    无前驱, 说明是根结点
178         // node.parent...right == node 找到【某结点为父节点或祖父节点的右子树中】
179         // 那么父节点就是某节点的前驱节点
180         return node.parent;
181     }
182     /**
183      * 后继节点: 中序遍历时的后一个节点
184      * 求后继节点
185      */
186     protected Node<E> successor(Node<E> node) {
187         if (node == null) return null;
188         // 后继节点与前驱节点正好相反
189 
190         // 后继节点在右子树中(node.right.left.left...)
191         if (node.right != null) {
192             Node<E> p = node.right;
193             while (p.left != null) {
194                 p = p.left;
195             }
196             return p;
197         }
198 
199         // 来到这里说明没有右节点, 则从父节点、祖父节点中寻找后继节点
200         // 当父节点不为空, 且某节点为父节点的右子节点
201         // 则顺着父节点找, 直到找到【某结点在父节点或祖父节点的左子树中】时
202         while (node.parent != null && node.parent.right == node) {
203             node = node.parent;
204         }
205 
206         // 来到这里有以下两种情况:
207         // node.parent == null 无前驱,说明是根结点
208         // node.parent.left == node 找到【某结点在父节点或祖父节点的左子树中】
209         // 那么父节点就是某节点的后继节点
210         return node.parent;
211     }
212     /**
213      * 根据传入的值删除元素
214      */
215     public void remove(E element) {
216         remove(node(element));
217     }
218     // 根据节点删除元素
219     private void remove(Node<E> node) {
220         if (node == null) return;
221 
222         size--;
223 
224         if (node.hasTwochildren()) { // 度为2的节点
225             // 找到后继节点
226             Node<E> s = successor(node);
227             // 用后继节点的值覆盖度为2的节点的值
228             node.element = s.element;
229             // 删除后继节点
230             node = s;
231         }
232 
233         // 删除node节点(node的度必然是1或者0)
234         Node<E> replacement = node.left != null ? node.left : node.right;
235 
236         if (replacement != null) { // node是度为1的节点
237             // 更改parent
238             replacement.parent = node.parent;
239             // 更改parent的left、right的指向
240             if (node.parent == null) { // node是度为1的节点并且是根节点
241                 root = replacement;
242             } else if (node == node.parent.left) {
243                 node.parent.left = replacement;
244             } else { // node == node.parent.right
245                 node.parent.right = replacement;
246             }
247         } else if (node.parent == null) { // node是叶子节点并且是根节点
248             root = null;
249         } else { // node是叶子节点,但不是根节点
250             if (node == node.parent.left) {
251                 node.parent.left = null;
252             } else { // node == node.parent.right
253                 node.parent.right = null;
254             }
255         }
256     }
257 
258     // 根据元素值获取节点元素
259     private Node<E> node(E element){
260         elementnotnullcheck(element);
261 
262         Node<E> node = root;
263         while(node != null){
264             int cmp = compare(element, node.element);
265             if(cmp < 0){
266                 node = node.left;//从左边找
267             }else if (cmp > 0){
268                 node = node.right;
269             }else{ // cmp == 0
270                 return node;
271             }
272         }
273         return null;
274     }
275 
276     /**
277      * 递归来计算高度
278      * @return
279      */
280     public  int height2(){
281         return height(root);
282     }
283 
284     private  int height(Node<E> node){
285         if(node==null) return 0;
286         return 1+Math.max(height( node.left) ,height(node.right)  );
287     }
288     /**
289      * 层序遍历来计算高度
290      */
291     public int height() {
292         if (root == null) return 0;
293 
294         // 树的高度
295         int height = 0;
296         // 存储着每一层的元素数量
297         int levelSize = 1;
298         Queue<Node<E>> queue = new LinkedList<>();
299         queue.offer(root);
300 
301         while (!queue.isEmpty()) {
302             Node<E> node = queue.poll();
303             levelSize--;
304 
305             if (node.left != null) {
306                 queue.offer(node.left);
307             }
308 
309             if (node.right != null) {
310                 queue.offer(node.right);
311             }
312 
313             if (levelSize == 0) { // 意味着即将要访问下一层
314                 levelSize = queue.size();
315                 height++;
316             }
317         }
318 
319         return height;
320     }
321     /**
322      * 是否是完全二叉树
323      */
324     public boolean isComplete() {
325         if (root == null) return false;
326 
327         Queue<Node<E>> queue = new LinkedList<>();
328         queue.offer(root);
329 
330         // leaf代表是否要求后面都是叶子节点
331         // 比如遍历到一个节点 left == null && right == null
332         //  或者是 left != null && right == null
333         // 则要求这个节点后面的节点都是叶子节点
334         boolean leaf = false;
335         while (!queue.isEmpty()) {
336             Node<E> node = queue.poll();
337             // 要求是叶子结点,但是当前节点不是叶子结点
338             if (leaf && !node.isleaf()) {
339                 return false;
340             }
341             if (node.left != null) {
342                 queue.offer(node.left);
343             } else if (node.right != null) {
344                 // node.left == null && node.right != null
345                 return false;
346             }
347             if (node.right != null) {
348                 queue.offer(node.right);
349             } else {
350                 // node.left == null && node.right == null
351                 // node.left != null && node.right == null
352                 leaf = true; // 要求后面都是叶子节点
353             }
354         }
355         return true;
356     }
357  //这些都为优化前
358 //    public boolean isComplete(){
359 //        if (root == null ) return false;
360 //        Queue<Node<E>> queue = new LinkedList<>();
361 //        queue.offer(root);
362 ////树不为空,开始遍历,入队。 1.左右不为空 ,入队2.左空,右不空,false 3.左不空,右空,或者左右都是空,那么右边都是叶子节点
363 //        boolean leaf=false;
364 //        while (!queue.isEmpty()) {
365 //            Node<E> node = queue.poll();
366 //            if(leaf&&!node.isleaf() ){return false;}
367 //            if (node.hasTwochildren() ){
368 //                queue.offer(node.left);
369 //                queue.offer(node.right);
370 //            }
371 //            else if (node.left == null&&node.right!=null) {
372 //                return  false;
373 //            }
374 //            else {//后面的遍历节点都是叶子
375 //                leaf=true;
376 //                if(node.left!=null)
377 //                    queue.offer(node.left);
378 //            }
379 //        }
380 //        return true;
381 //    }
382 //
383 //    /**
384 //     * 前序遍历
385 //     */
386 //    public void preorderTraversal(){
387 //        preorderTraversal(root);
388 //    }
389 //    private void preorderTraversal(Node<E> node){
390 //        if(node==null) return;
391 //         System.out.println(node.element);
392 //         preorderTraversal(node.left);
393 //         preorderTraversal(node.right);
394 //
395 //        }
396 //    /**
397 //     * 中序遍历
398 //     */
399 //    public void inorderTraversal(){
400 //inorderTraversal(root);
401 //    }
402 //
403 //
404 //    private void inorderTraversal(Node<E> node){
405 //        if(node==null) return;
406 //        preorderTraversal(node.left);
407 //        System.out.println(node.element);
408 //        preorderTraversal(node.right);
409 //
410 //    }
411 ///**
412 // * 后续遍历
413 // */
414 //        public void postorderTraversal(){
415 //             postorderTraversal(root);
416 //}
417 //
418 //
419 //        private void postorderTraversal(Node<E> node){
420 //        if(node==null) return;
421 //                    postorderTraversal(node.left);
422 //        postorderTraversal(node.right);
423 //        System.out.println(node.element);
424 //
425 //    }
426 //
427 //        /**
428 //     * 层序遍历
429 //     */
430 //    public void levelOrderTraversal() {
431 //        if (root == null) return;
432 //
433 //        Queue<Node<E>> queue = new LinkedList<>();
434 //        queue.offer(root);
435 //
436 //        while (!queue.isEmpty()) {
437 //            Node<E> node = queue.poll();
438 //            System.out.println(node.element);
439 //
440 //            if (node.left != null) {
441 //                queue.offer(node.left);
442 //            }
443 //
444 //            if (node.right != null) {
445 //                queue.offer(node.right);
446 //            }
447 //        }
448 //    }
449 
450     /**
451      *
452      * @param e1
453      * @param e2
454      * @return ==0 相等  大于0 e1大
455      */
456     private int compare(E e1,E e2){
457         if(comparator!=null){
458             return comparator.compare(e1,e2);
459 
460         }
461         return ((Comparable<E>)e1).compareTo(e2);
462      }
463 
464 
465 
466     @Override
467     public Object root() {
468         return root;
469     }
470 
471     @Override
472     public Object left(Object node) {
473         return ((Node<E>)node).left;
474     }
475 
476     @Override
477     public Object right(Object node) {
478         return ((Node<E>)node).right;
479     }
480 
481     @Override
482     public Object string(Object node) {
483         return ((Node<E>)node).element;
484     }
485 //定位节点
486     private static class Node<E>{
487         E element;
488         Node<E> left;//左子节点
489         Node<E> right;
490         Node<E> parent;//父节点
491         public Node (E element,Node<E> parent){
492             this.element=element;
493             this.parent=parent;
494         }
495     public boolean isleaf(){
496             return left==null&&right==null;
497     }
498     public boolean hasTwochildren(){
499         return left!=null&&right!=null;
500 
501     }
502 
503 }
504 }

2.二叉树的通用类(父类)

  1 package DataStructure.Tree_new;
  2 
  3 import DataStructure.Tree.printer.BinaryTreeInfo;
  4 
  5 import java.util.LinkedList;
  6 import java.util.Queue;
  7 
  8 public class BinaryTree <E>implements BinaryTreeInfo   {
  9     protected int size; // 元素数量
 10     protected Node<E> root; // 根节点
 11 
 12     /**
 13      * 访问器接口 ——> 访问器抽象类
 14      * 增强遍历接口
 15      */
 16     /*public static interface Visitor<E>{
 17         void visit(E element);
 18     }*/
 19     public static abstract class Visitor<E> {
 20         boolean stop;
 21         // 如果返回true,就代表停止遍历
 22         public abstract boolean visit(E element);
 23     }
 24 
 25     /**
 26      * 内部类,节点类
 27      */
 28     public static class Node<E> {
 29         E element;      // 元素值
 30         Node<E> left;   // 左节点
 31         Node<E> right;  // 右节点
 32         Node<E> parent; // 父节点
 33 
 34         public Node(E element, Node<E> parent) {
 35             this.element = element;
 36             this.parent = parent;
 37         }
 38 
 39         public boolean isLeaf() { // 是否叶子节点
 40             return left == null && right == null;
 41         }
 42 
 43         public boolean hasTwoChildren() { // 是否有两个子节点
 44             return left != null && right != null;
 45         }
 46         public boolean isLeftChild(){ // 判断自己是不是左子树
 47             return parent!=null && this==parent.left;
 48         }
 49         public boolean isRightChild(){ // 判断自己是不是右子树
 50             return parent!=null && this==parent.right;
 51         }
 52         /*
 53          * 返回兄弟节点
 54          */
 55         public Node<E> sibling() { // 红黑树中用到, 返回兄弟节点
 56             if (isLeftChild()) {
 57                 return parent.right;
 58             }
 59 
 60             if (isRightChild()) {
 61                 return parent.left;
 62             }
 63             return null;
 64         }
 65     }
 66 
 67 
 68     /**
 69      * 元素的数量
 70      */
 71     public int size() {
 72         return size;
 73     }
 74 
 75     /**
 76      * 是否为空
 77      */
 78     public boolean isEmpty() {
 79         return size == 0;
 80     }
 81 
 82     /**
 83      * 清空所有的元素
 84      */
 85     public void clear() {
 86         root = null;
 87         size = 0;
 88     }
 89 
 90     /**
 91      * 前序遍历
 92      */
 93     public void preorder(Visitor<E> visitor) {
 94         if (visitor == null) return;
 95         preorder(root, visitor);
 96     }
 97     public void preorder(Node<E> node, Visitor<E> visitor) {
 98         if (node == null || visitor.stop) return;
 99         //
100         visitor.stop = visitor.visit(node.element);
101         //
102         preorder(node.left, visitor);
103         //
104         preorder(node.right, visitor);
105     }
106 
107     /**
108      * 中序遍历
109      */
110     public void inorder(Visitor<E> visitor) {
111         if (visitor == null) return;
112         inorder(root, visitor);
113     }
114     public void inorder(Node<E> node, Visitor<E> visitor) {
115         if (node == null || visitor.stop) return;
116         //
117         inorder(node.left, visitor);
118         //
119         if (visitor.stop) return;
120         visitor.stop = visitor.visit(node.element);
121         //
122         inorder(node.right, visitor);
123     }
124 
125     /**
126      * 后序遍历
127      */
128     public void postorder(Visitor<E> visitor) {
129         if (visitor == null) return;
130         postorder(root, visitor);
131     }
132     public void postorder(Node<E> node, Visitor<E> visitor) {
133         if (node == null || visitor.stop) return;
134         //
135         postorder(node.left, visitor);
136         //
137         postorder(node.right, visitor);
138         //
139         if (visitor.stop) return;
140         visitor.stop = visitor.visit(node.element);
141     }
142 
143     /**
144      * 层次遍历
145      */
146     public void levelOrder(Visitor<E> visitor) {
147         if (root == null || visitor.stop) return;
148         Queue<Node<E>> queue = new LinkedList<>(); // 队列
149         queue.offer(root);
150 
151         while (!queue.isEmpty()) {
152             Node<E> node = queue.poll();
153             if (visitor.visit(node.element)) return;
154 
155             if (node.left != null) {
156                 queue.offer(node.left);
157             }
158             if (node.right != null) {
159                 queue.offer(node.right);
160             }
161         }
162     }
163 
164     /**
165      * 求树的高度(递归)
166      */
167     public int height1() {
168         return height1(root);
169     }
170     public int height1(Node<E> node) {
171         if (node == null) return 0;
172         return 1 + Math.max(height1(node.left), height1(node.right));
173     }
174 
175     /**
176      * 求树的高度高度(迭代)
177      */
178     public int height() {
179         if (root == null) return 0;
180         int levelSize = 1; // 存储每一层的元素数量
181         int height = 0; // 树的高度
182         Queue<Node<E>> queue = new LinkedList<>();
183         queue.offer(root);
184 
185         while (!queue.isEmpty()) {
186             Node<E> node = queue.poll();
187             levelSize--;
188             if (node.left != null) {
189                 queue.offer(node.left);
190             }
191             if (node.right != null) {
192                 queue.offer(node.right);
193             }
194             if (levelSize == 0) { // 即将要访问下一层
195                 levelSize = queue.size();
196                 height++;
197             }
198         }
199         return height;
200     }
201 
202     /**
203      * 是否是完全二叉树
204      */
205     public boolean isComplete() {
206         if (root == null) return false;
207 
208         Queue<Node<E>> queue = new LinkedList<>();
209         queue.offer(root);
210 
211         // leaf代表是否要求后面都是叶子节点
212         // 比如遍历到一个节点 left == null && right == null
213         //  或者是 left != null && right == null
214         // 则要求这个节点后面的节点都是叶子节点
215         boolean leaf = false;
216         while (!queue.isEmpty()) {
217             Node<E> node = queue.poll();
218             // 要求是叶子结点,但是当前节点不是叶子结点
219             if (leaf && !node.isLeaf()) {
220                 return false;
221             }
222             if (node.left != null) {
223                 queue.offer(node.left);
224             } else if (node.right != null) {
225                 // node.left == null && node.right != null
226                 return false;
227             }
228             if (node.right != null) {
229                 queue.offer(node.right);
230             } else {
231                 // node.left == null && node.right == null
232                 // node.left != null && node.right == null
233                 leaf = true; // 要求后面都是叶子节点
234             }
235         }
236         return true;
237     }
238 
239     /**
240      * 前驱节点: 中序遍历时的前一个节点
241      * 求前驱节点
242      */
243     protected Node<E> predecessor(Node<E> node) {
244         if (node == null) return null;
245 
246         // 前驱节点在左子树中(left.right.right.right....)
247         Node<E> p = node.left;
248         if (p != null) {
249             // 左子树不为空,则找到它的最右节点
250             while (p.right != null) {
251                 p = p.right;
252             }
253             return p;
254         }
255 
256         // 能来到这里说明左子树为空, 则从父节点、祖父节点中寻找前驱节点
257         // 当父节点不为空, 且某节点为父节点的左子节点
258         // 则顺着父节点找, 直到找到【某结点为父节点或祖父节点的右子树中】时
259         while (node.parent != null && node.parent.left == node) {
260             node = node.parent;
261         }
262 
263         // 来到这里有以下两种情况:
264         // node.parent == null 无前驱, 说明是根结点
265         // node.parent...right == node 找到【某结点为父节点或祖父节点的右子树中】
266         // 那么父节点就是某节点的前驱节点
267         return node.parent;
268     }
269 
270     /**
271      * 后继节点: 中序遍历时的后一个节点
272      * 求后继节点
273      */
274     protected Node<E> successor(Node<E> node) {
275         if (node == null) return null;
276         // 后继节点与前驱节点正好相反
277 
278         // 后继节点在右子树中(node.right.left.left...)
279         if (node.right != null) {
280             Node<E> p = node.right;
281             while (p.left != null) {
282                 p = p.left;
283             }
284             return p;
285         }
286 
287         // 来到这里说明没有右节点, 则从父节点、祖父节点中寻找后继节点
288         // 当父节点不为空, 且某节点为父节点的右子节点
289         // 则顺着父节点找, 直到找到【某结点在父节点或祖父节点的左子树中】时
290         while (node.parent != null && node.parent.right == node) {
291             node = node.parent;
292         }
293 
294         // 来到这里有以下两种情况:
295         // node.parent == null 无前驱,说明是根结点
296         // node.parent.left == node 找到【某结点在父节点或祖父节点的左子树中】
297         // 那么父节点就是某节点的后继节点
298         return node.parent;
299     }
300 
301     /**
302      * 创建节点的方法,用于给AVL树创建节点
303      */
304     protected Node<E> createNode(E element, Node<E> parent){
305         return new Node<>(element, parent); // 默认返回一个通用节点
306     }
307 
308     /**
309      * BinaryTreeInfo 工具,用来打印二叉树
310      */
311     @Override
312     public Object root() {
313         return root;
314     }
315 
316     @Override
317     public Object left(Object node) {
318         return ((Node<E>) node).left;
319     }
320 
321     @Override
322     public Object right(Object node) {
323         return ((Node<E>) node).right;
324     }
325 
326     @Override
327     public Object string(Object node) {
328         Node<E> myNode = (Node<E>) node;
329         String parentStr = "null";
330         if (myNode.parent != null) {
331             parentStr = myNode.parent.element.toString();
332         }
333         return myNode.element + "_p(" + parentStr + ")";
334     }
335 }

3.二叉搜索树

  1 package DataStructure.Tree_new;
  2 
  3 import java.util.Comparator;
  4 
  5 public class BST<E> extends BinaryTree<E> { // 继承通用二叉树类
  6 
  7     // 比较器,根据传入的比较器实现 compareTo() 方法
  8     private Comparator<E> comparator;
  9 
 10     public BST(Comparator<E> comparator) { // 可以传一个比较器
 11         this.comparator = comparator;
 12     }
 13 
 14     public BST() { // 不传比较器,相当于传入一个 null
 15         this(null); //
 16     }
 17 
 18     /**
 19      * 是否包含某元素
 20      */
 21     public boolean contains(E element) {
 22         return node(element) != null;
 23     }
 24 
 25     /**
 26      * 添加元素
 27      */
 28     public void add(E element) {
 29         elementNotNullCheck(element); // 不能传入空节点
 30 
 31         // 传入第一个节点, 若根节点为null, 则该节点为根节点
 32         if (root == null) {
 33             root = new Node<>(element, null);
 34             size++;
 35             return;
 36         }
 37 
 38         // 添加的不是第一个节点, 找到父节点
 39         Node<E> node = root;
 40         Node<E> parent = root;
 41         int cmp = 0;
 42         while (node != null) {
 43             parent = node; // 父节点
 44             // 比较【传入节点的元素值】与【父节点的元素值】
 45             cmp = compareTo(element, node.element);
 46             if (cmp > 0) { // 传入节点比父节点要大, 往右
 47                 node = node.right;
 48             } else if (cmp < 0) { // 传入节点比父节点要小, 往左
 49                 node = node.left;
 50             } else { // 相等, 最好是覆盖掉, 也可以采取其他操作, 看业务需求
 51                 node.element = element;
 52                 return;
 53             }
 54         }
 55 
 56         // 上面只是找到了要添加位置的父节点, 下面要将元素添加进去
 57         Node<E> newNode = new Node<>(element, parent);
 58         if (cmp > 0) {
 59             parent.right = newNode;
 60         } else {
 61             parent.left = newNode;
 62         }
 63         size++;
 64     }
 65 
 66     /**
 67      * 根据传入的值删除元素
 68      */
 69     public void remove(E element) {
 70         remove(node(element));
 71     }
 72     // 根据节点删除元素
 73     private void remove(Node<E> node) {
 74         if (node == null) return;
 75 
 76         size--;
 77 
 78         if (node.hasTwoChildren()) { // 度为2的节点
 79             // 找到后继节点
 80             Node<E> s = successor(node);
 81             // 用后继节点的值覆盖度为2的节点的值
 82             node.element = s.element;
 83             // 删除后继节点
 84             node = s;
 85         }
 86 
 87         // 删除node节点(node的度必然是1或者0)
 88         Node<E> replacement = node.left != null ? node.left : node.right;
 89 
 90         if (replacement != null) { // node是度为1的节点
 91             // 更改parent
 92             replacement.parent = node.parent;
 93             // 更改parent的left、right的指向
 94             if (node.parent == null) { // node是度为1的节点并且是根节点
 95                 root = replacement;
 96             } else if (node == node.parent.left) {
 97                 node.parent.left = replacement;
 98             } else { // node == node.parent.right
 99                 node.parent.right = replacement;
100             }
101         } else if (node.parent == null) { // node是叶子节点并且是根节点
102             root = null;
103         } else { // node是叶子节点,但不是根节点
104             if (node == node.parent.left) {
105                 node.parent.left = null;
106             } else { // node == node.parent.right
107                 node.parent.right = null;
108             }
109         }
110     }
111 
112     // 根据元素值获取节点元素
113     private Node<E> node(E element) {
114         elementNotNullCheck(element);
115 
116         Node<E> node = root;
117         while (node != null) {
118             int cmp = compareTo(element, node.element);
119             if (cmp < 0) {
120                 node = node.left;
121             } else if (cmp > 0) {
122                 node = node.right;
123             } else { // cmp == 0
124                 return node;
125             }
126         }
127         return null;
128     }
129 
130     // 节点元素比较
131     private int compareTo(E e1, E e2) {
132         if (comparator != null) { // 传入比较器则通过比较器比较
133             return comparator.compare(e1, e2);
134         }
135         // 没传比较器,元素内部必须自行实现了 Comparable 接口
136         return ((Comparable<E>) e1).compareTo(e2);
137     }
138 
139     // 检测传入的节点是否为空
140     private void elementNotNullCheck(E element) {
141         if (element == null) { // 不能传入空节点
142             throw new IllegalArgumentException("element must not null");
143         }
144     }
145 
146 }

 

 posted on 2021-09-23 21:00  “樂·~  阅读(58)  评论(0编辑  收藏  举报