首页 > 代码库 > 一步一图一代码之排序二叉树

一步一图一代码之排序二叉树

<script type="text/javascript" src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script><script type="text/javascript">//

‘; j+=‘
‘; j+=‘

目录(?)[+]

‘; j+=‘
    ‘; var g=0,f=0; for(var c=0;cg){ j+="
      ";f++ }else{ if(e0){ j+="
    ";f-- } } if(e==1){ while(f>0){ j+="
";f-- } } g=e; var b=d.eq(c).text().replace(/^[\d.、\s]+/g,""); b=b.replace(/[^a-zA-Z0-9_\-\s\u4e00-\u9fa5]+/g,""); if(b.length<100){ j+=‘
  • ‘+b+"
  • "; d.eq(c).html(‘‘+d.eq(c).html()) } } while(f>0){ j+="";f-- } j+="
    ";j+=‘
    ‘; $(j).insertBefore($("#article_content")) }); function openct(b){ if(b.innerHTML=="[+]"){ $(b).attr("title","收起").html("[-]").parent().next().show() } else{ $(b).attr("title","展开").html("[+]").parent().next().hide() }b.blur(); return false }// ]]></script>

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong/)

    属性:

    ①若它的左子树不为空,则左子树上所有节点的值均小于它的根节点的值。

    ②若它的右子树不为空,则右子树上所有节点的值均大于它的根节点的值。

    ③它的左、右子树也都是排序二叉树。

    添加操作:

    当根节点为空时,添加进的节点作为根节点。然后每次添加节点时,都从根节点过滤,以根节点作为当前节点,如果新节点大于当前节点,则走当前节点的右子节点分支,如果新节点小于当前节点,则走当前节点的左子节点分支。然后再用右子节点或左子节点作为当前节点与新加节点比较,如果新加节点大,则当前节点的右子节点,如果新加节点小,则当前节点的左子节点。依次类推直到左子树或右子树为null时,将该新节点添加在这里。

    图解:

    技术分享

    代码解析:

    技术分享
     1 public void add(T data){ 2     Node newNode=new Node(data); 3     if(root==null){ 4         root=newNode; 5     }else { 6         add(newNode, root); 7     } 8 } 9 public void add(Node addNode,Node parentNode) {10     T data=http://www.mamicode.com/addNode.data;11     Node currentNode=parentNode;12     Node parent=null;13     int flag=0;14     while(currentNode!=null){15         parent=currentNode;16         flag=data.compareTo(currentNode.data);17         if(flag>0){18             currentNode=currentNode.rightChild;19         }else {20             currentNode=currentNode.leftChild;21         }22     }23     if(flag>0){24         parent.rightChild=addNode;25     }else {26         parent.leftChild=addNode;27     }28     addNode.parent=parent;        29 }
    View Code

    添加操作相对简单,麻烦的是删除操作。

    删除操作

    该操作有很多的情况:

          第一:待删除的节点是叶子节点

          第二:待删除的节点有左子树没有右子树

          第三:待删除的节点没有左子树有右子树

          第四:待删除的节点既没有左子树也没有右子树。

    图解

    在第一大类中我们还可分:

    1、该叶子节点就是根节点,即树中只有一个根节点。

    这是最简单的情况,直接树的根root指向null即可。

    技术分享

    2、否则便要判断该叶子节点是其父节点的左子节点呢还是其父节点的右子节点

    下图为叶子节点是其父节点的左子节点,右子节点一样。

    技术分享

    在第二大类中我们还可以细分:

    1、待删除的节点就是根节点。

    只需将根节点的左子节点(因为这是根节点只有左子节点)指向父节点的指针打断,并将该左子节点设为根节点即可。

    技术分享

    2、如果待删除的节点不是根节点,那就必须讨论待删除的节点是其父节点的左子节点还是右子节点。

    以是其父节点的左子节点为例(右子节点的情况一样),我们需要让待删除节点的父节点的指向其左孩子的指针重新指向待删除节点的左孩子,让待删除节点的左孩子的指向父节点的指针重新指向待删除节点的父节点。

    技术分享

    这期间,我们并没有打断图中节点5指向其左孩子节点3的指针,也没有打断节点5指向其父节点6的指针。这是因为,没这个必要。我们不打断这些节点5永远也不会被访问到,而且会被垃圾回收。

    第三大类和第二大类一样。

    第四大类

    总的思想就是:找到待删除节点左子树中的最大节点(leftMaxNode),然后用leftMaxNode替代待删除的节点即可。这句话看似简单,但是实现起来可不是那么的容易!!!

    我们也可以细分:

    1、待删除节点就是根节点

         1.1 leftMaxNode就是待删除节点的左孩子。即待删除节点左子树中没有右节点。

    技术分享

         1.2 如果待删除的节点不是根节点

    对于这种情况,我们需要显示显示打断的“指针”只有两个:如下图中,节点3指向其右子节点4(leftMaxNode)的指针,即节点4(leftMaxNode)指向其父节点3的指针。然后用节点4(leftMaxNode)替换节点5(根节点)即可。

    技术分享

    2、待删除的节点不是根节点

         2.1 被删除节点的左子树中最大节点不是其左子节点

    如下图所示:

    技术分享

    在这种情况下使用节点节点4替换节点5的时候,对于节点4和节点8、节点9的之间的“指针”创建比较容易,但是节点3与节点4之间的“指针”却不好创建,因为节点4的左子树可能很复杂,我们不知道将节点3插入哪里合适,所以,解决办法为,将节点3利用排序二叉树添加新节点的功能以新节点的形式加入节点4 中。

    技术分享

         2.2 被删除节点的左子树中最大节点就是是其左子节点

    即,如下情况,

    技术分享

    这种情况很简单了,就不详细说明了。

    完整代码

    技术分享
      1 package com.yyl.tree;  2   3 import java.util.ArrayDeque;  4 import java.util.ArrayList;  5 import java.util.List;  6 import java.util.Queue;  7   8 public class SortedBinaryTree<T extends Comparable<T>> {  9     public class Node{ 10         T data=http://www.mamicode.com/null; 11         Node parent=null; 12         public Node getParent() { 13             return parent; 14         } 15         public void setParent(Node parent) { 16             this.parent = parent; 17         } 18  19         Node leftChild=null; 20         Node rightChild=null; 21         //region【访问器】 22         public T getData() { 23             return data; 24         } 25         public void setData(T data) { 26             this.data =http://www.mamicode.com/ data; 27         } 28         public Node getLeftChild() { 29             return leftChild; 30         } 31         public void setLeftChild(Node leftChild) { 32             this.leftChild = leftChild; 33         } 34         public Node getRightChild() { 35             return rightChild; 36         } 37         public void setRightChild(Node rightChild) { 38             this.rightChild = rightChild; 39         } 40         //endregion 41          42         // region 【构造器】 43         public Node(){} 44         public Node(T data){ 45             this.data=http://www.mamicode.com/data; 46         } 47         public Node(T data, Node leftChild, Node rightChild){ 48             this.data=http://www.mamicode.com/data; 49             this.leftChild=leftChild; 50             this.rightChild=rightChild; 51         } 52         //endregion 53         @Override 54         public boolean equals(Object obj) { 55             if(this.data=http://www.mamicode.com/=obj){ 56                 return true; 57             }else if(this.getClass()==obj.getClass()){ 58                 Node nodeObj=(Node)obj; 59                  return this.data.compareTo(nodeObj.data) ==0 && this.leftChild.equals(nodeObj.leftChild)  60                         && this.rightChild.equals(nodeObj); 61             } 62             return false; 63         } 64      65         public String toString(){ 66             return "[data:"+this.data+"]"; 67         } 68     } 69      70     Node root=null; 71     public SortedBinaryTree() {} 72     public SortedBinaryTree(Node root){ 73         this.root=root; 74     } 75      76     //添加节点 77     public void add(T data){ 78         Node newNode=new Node(data); 79         if(root==null){ 80             root=newNode; 81         }else { 82             add(newNode, root); 83         } 84     } 85     public void add(Node addNode,Node parentNode) { 86         T data=http://www.mamicode.com/addNode.data; 87         Node currentNode=parentNode; 88         Node parent=null; 89         int flag=0; 90         while(currentNode!=null){ 91             parent=currentNode; 92             flag=data.compareTo(currentNode.data); 93             if(flag>0){ 94                 currentNode=currentNode.rightChild; 95             }else { 96                 currentNode=currentNode.leftChild; 97             } 98         } 99         if(flag>0){100             parent.rightChild=addNode;101         }else {102             parent.leftChild=addNode;103         }104         addNode.parent=parent;        105     }106     // 删除节点107     public void remove(T data){108         if(root==null){109             return;110         }else {111             Node removeNode=null;//待删除的节点112             removeNode = getNode(data);113             //endregion114             115             //如果没有找见抛出异常116             if(removeNode==null){117                 throw new RuntimeException("未找见要删除的节点");118             }119             System.out.println("待删除的节点为:"+removeNode.toString());120             //如果待删除的节点既没有左孩子也没有右孩子121             if(removeNode.leftChild==null && removeNode.rightChild==null){122                 if(removeNode==root){//如果待删除的节点就是根节点,即树中只有一个根节点123                     root=null;124                 }else {//如果待删除的节点不是根节点,这就要考虑这个节点是其父节点的左孩子还是右孩子125                     if(isLeftNodeOfParent(removeNode)){//如果待删除是其父节点的左孩子,则断开其父节点指向左孩子的“指针”126                         removeNode.parent.leftChild=null;127                     }else {//如果待删除是其父节点的有孩子,则断开其父节点指向右孩子的“指针”128                         removeNode.parent.rightChild=null;129                     }130                     //【注】断开他的指向父节点的“指针”131                     //removeNode.parent=null;132                 }133             }else if (removeNode.leftChild!=null && removeNode.rightChild==null) {//如果待删除的节点有左孩子但是没有右孩子134                 if(removeNode==root){//如果待删除的节点就是根节点135                     136                     //这条语句并不是必须的,因为对于数而言,不管怎么都是以root为起点开始所有操作,所以遍历等操作不会涉及137                     //到root.leftChild.parent.不将它设为null,同样不影响树的正常运行。但是root.leftChild.parent所引138                     //用的对象并不会被当成垃圾而被回收,因为从root.leftChild.parent依然可以访问到该Java对象139                     root.leftChild.parent=null;140                     141                     root=root.leftChild;142                 }else {//如果待删除的节点不是根节点,那就必须讨论待删除的节点是其父节点的左子节点还是右子节点143                     if(isLeftNodeOfParent(removeNode)){144                         removeNode.parent.leftChild=removeNode.leftChild;145                     }else {146                         removeNode.parent.rightChild=removeNode.leftChild;147                     }148                     //这条语句不要更精炼,加上会让程序更清晰149                     //removeNode.parent=null;        150                     151                     152                     removeNode.leftChild.parent=removeNode.parent;                153                 }154                 //这条语句不要更精炼,加上会让程序更清晰155                 //removeNode.leftChild=null;156             }157             else if (removeNode.leftChild==null && removeNode.rightChild!=null) {//如果待删除的节点没有左孩子但是有右孩子158                 if(root==removeNode){//如果待删除的节点就是根节点159                     root=root.rightChild;160                     //释放内存,让垃圾回收removeNode161                     removeNode.rightChild.parent=null;                    162                 }else {//如果待删除的节点不是根节点163                     if(isLeftNodeOfParent(removeNode)){164                         removeNode.parent.leftChild=removeNode.rightChild;165                     }else {166                         removeNode.parent.rightChild=removeNode.rightChild;167                     }                168                     removeNode.rightChild.parent=removeNode.parent;169                     170                     //这条语句不要更精炼,加上会让程序更清晰171                     //removeNode.parent=null;172                 }173                 //这条语句不要更精炼,加上会让程序更清晰174                 //removeNode.rightChild=null;175             }else {//如果待删除的节点既有左孩子也有右孩子176                 Node leftMaxNode=null;177                 //region【寻找被删除节点左子树中最大节点】                178                 Node tmp=removeNode.leftChild;179                 while(tmp.rightChild!=null){                    180                     tmp=tmp.rightChild;                181                 }182                 leftMaxNode=tmp;183                 //endregion184                 185                 System.out.println("leftmaxnode:"+leftMaxNode.toString());186                 if(removeNode==root){//如果待删除的节点就是根节点187                     if(leftMaxNode==removeNode.leftChild){//如果被删除节点的左子树中最大节点就是其左子节点            188                         leftMaxNode.rightChild=root.rightChild;189                         root.rightChild.parent=leftMaxNode;190                         root=leftMaxNode;191                         leftMaxNode.parent=null;192                         193                         //这条语句不要更精炼,加上会让程序更清晰194                         //root.leftChild=null;195                     }else {196                         leftMaxNode.rightChild=root.rightChild;    197                         leftMaxNode.leftChild=root.leftChild;198                         199                         root.rightChild.parent=leftMaxNode;200                         root.leftChild.parent=leftMaxNode;201                         202                         leftMaxNode.parent.rightChild=null;203                         leftMaxNode.parent=null;                        204                         root=leftMaxNode;205                     }206                 }else {//如果待删除的节点不是根节点207                     if(leftMaxNode!=removeNode.leftChild){//如果被删除节点的左子树中最大节点不是其左子节点                        208                         Node addNode=removeNode.leftChild;209                         removeNode.leftChild=null;210                         addNode.parent=null;211                         add(addNode,leftMaxNode);    212                         leftMaxNode.parent.rightChild=null;213                     }    214                     leftMaxNode.rightChild=removeNode.rightChild;            215                     removeNode.rightChild.parent=leftMaxNode;    216                     217                     leftMaxNode.parent=removeNode.parent;    218                     219                     220                     if(isLeftNodeOfParent(removeNode)){221                         removeNode.parent.leftChild=leftMaxNode;222                     }else {                    223                         removeNode.parent.rightChild=leftMaxNode;                        224                     }                    225                 }226             }227         }228     }229     230     public Node getNode(T data) {231         Node removeNode=null;232         Node current=root;//临时变量,用于辅助查找待删除的节点            233         //region 【寻找要待删除的节点】234         while(current!=null){235             if(data.compareTo(current.data)>0){236                 current=current.rightChild;237             }else if (data.compareTo(current.data)<0) {238                 current=current.leftChild;239             }else {240                 removeNode=current;241                 break;242             }243         }244         return removeNode;245     }246     247     private boolean isLeftNodeOfParent(Node node) {248         return node.data.compareTo(node.parent.data)<=0;249     }250     //中序遍历251     public List<Node> midIterator(){252         return midIterator(root);253     }254     private List<Node> midIterator(Node node) {255         List<Node> list=new ArrayList<Node>();256         if(node==null){257             return null;258         }259         if(node.leftChild!=null){260             list.addAll(midIterator(node.leftChild));261         }262         list.add(node);263         if(node.rightChild!=null){264             list.addAll(midIterator(node.rightChild));265         }266         return list;267     }268 269     //先序遍历270     public List<Node> preIterator(){271         return preIterator(root);272     }273     private List<Node> preIterator(Node root) {274         List<Node> list=new ArrayList<Node>();275         if(root==null)276             return null;277         list.add(root);278         if(root.leftChild!=null){279             list.addAll(preIterator(root.leftChild));280         }281         if(root.rightChild!=null){282             list.addAll(preIterator(root.rightChild));283         }284         return list;285     }286 287     //广度优先遍历288     public List<Node> breadthFirst(){289         return breadthFirst(root);290     }291     private List<Node> breadthFirst(Node root) {292         Queue<Node> queue=new ArrayDeque<Node>();293         List<Node> list=new ArrayList<Node>();294         if(root==null)295             return null;296         queue.offer(root);297         while(!queue.isEmpty()){298             Node node=queue.poll();299             list.add(node);300             if(node.leftChild!=null){301                 queue.offer(node.leftChild);302             }303             if(node.rightChild!=null){304                 queue.offer(node.rightChild);305             }306         }307         return list;308     }309 }
    View Code

    可能有些地方理解的还是不到位,并且代码的性能不是很高,请大家批评指正。

    注:版权所有,转载请注明出处。

    一步一图一代码之排序二叉树