首页 > 代码库 > 一步一图一代码之排序二叉树
一步一图一代码之排序二叉树
<script type="text/javascript" src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script><script type="text/javascript">//
目录(?)[+]
‘; 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+=‘作者:禅楼望月(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 }
添加操作相对简单,麻烦的是删除操作。
删除操作
该操作有很多的情况:
第一:待删除的节点是叶子节点
第二:待删除的节点有左子树没有右子树
第三:待删除的节点没有左子树有右子树
第四:待删除的节点既没有左子树也没有右子树。
图解
在第一大类中我们还可分:
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 }
可能有些地方理解的还是不到位,并且代码的性能不是很高,请大家批评指正。
注:版权所有,转载请注明出处。
一步一图一代码之排序二叉树