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

作者:禅楼望月(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=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=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 = 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=data;
 46         }
 47         public Node(T data, Node leftChild, Node rightChild){
 48             this.data=data;
 49             this.leftChild=leftChild;
 50             this.rightChild=rightChild;
 51         }
 52         //endregion
 53         @Override
 54         public boolean equals(Object obj) {
 55             if(this.data==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=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             //endregion
114
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                     //释放内存,让垃圾回收removeNode
161                     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                 //endregion
184
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 }

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

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

时间: 2024-08-11 07:38:59

一步一图一代码之排序二叉树的相关文章

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

作者:禅楼望月(http://www.cnblogs.com/yaoyinglong/) 属性: ①若它的左子树不为空,则左子树上所有节点的值均小于它的根节点的值. ②若它的右子树不为空,则右子树上所有节点的值均大于它的根节点的值. ③它的左.右子树也都是排序二叉树. 添加操作: 当根节点为空时,添加进的节点作为根节点.然后每次添加节点时,都从根节点过滤,以根节点作为当前节点,如果新节点大于当前节点,则走当前节点的右子节点分支,如果新节点小于当前节点,则走当前节点的左子节点分支.然后再用右子节点

一步一图一代码,一定要让你真正彻底明白红黑树【转】

转自:http://blog.csdn.net/chenhuajie123/article/details/11951777 一步一图一代码,一定要让你真正彻底明白红黑树 作者:July   二零一一年一月九日 ----------------------------- 本文参考:I.  The Art of Computer Programming Volume III. Introduction to Algorithms, Second EditionIII.The Annotated S

01-(2)数据结构- 一步一步写算法(之之排序二叉树)

前面我们讲过双向链表的数据结构.每一个循环节点有两个指针,一个指向前面一个节点,一个指向后继节点,这样所有的节点像一颗颗珍珠一样被一根线穿在了一起.然而今天我们讨论的数据结构却有一点不同,它有三个节点.它是这样定义的: [cpp] view plain copy typedef struct _TREE_NODE { int data; struct _TREE_NODE* parent; struct _TREE_NODE* left_child; struct _TREE_NODE* rig

图论算法 有图有代码 万字总结 向前辈致敬

图的定义 背景知识 看到这篇博客相信一开始映入读者眼帘的就是下面这幅图了,这就是传说中的七桥问题(哥尼斯堡桥问题).在哥尼斯堡,普雷格尔河环绕着奈佛夫岛(图中的A岛).这条河将陆地分成了下面4个区域,该处还有着7座连接这些陆地的桥梁. 问题是如何从某地出发,依次沿着各个桥,必须经过每座桥且每座桥只能经过1次,最终回到原地. 不知道这个问题且好奇的童鞋现在肯定在忙活着找出来这道题的结果了. 是伟大的数学家欧拉(Leonhard Euler)在1736年首次使用图的方法解决了该问题. 欧拉将上面的模

一步一步写算法(之排序二叉树)

原文:一步一步写算法(之排序二叉树) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面我们讲过双向链表的数据结构.每一个循环节点有两个指针,一个指向前面一个节点,一个指向后继节点,这样所有的节点像一颗颗珍珠一样被一根线穿在了一起.然而今天我们讨论的数据结构却有一点不同,它有三个节点.它是这样定义的: typedef struct _TREE_NODE { int data; struct _TREE_NODE* parent; str

一步一步写算法(之排序二叉树线索化)

原文:一步一步写算法(之排序二叉树线索化) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面我们谈到了排序二叉树,还没有熟悉的同学可以看一下这个,二叉树基本操作.二叉树插入.二叉树删除1.删除2.删除3.但是排序二叉树也不是没有缺点,比如说,如果我们想在排序二叉树中删除一段数据的节点怎么办呢?按照现在的结构,我们只能一个一个数据查找验证,首先看看在不在排序二叉树中,如果在那么删除:如果没有这个数据,那么继续查找.那么有没有方法,可以保

一步一步写算法(之选择排序)

原文:一步一步写算法(之选择排序) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 选择排序是和冒泡排序差不多的一种排序.和冒泡排序交换相连数据不一样的是,选择排序只有在确定了最小的数据之后,才会发生交换.怎么交换呢?我们可以以下面一组数据作为测试: 2, 1, 5, 4, 9 第一次排序:1, 2, 5, 4, 9 第二次排序: 1, 2, 5, 4, 9 第三次排序: 1, 2, 4, 5, 9 第四次排序: 1, 2, 4, 5,

一步一步写算法(之排序二叉树删除-2)

原文:一步一步写算法(之排序二叉树删除-2) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 2.4 删除节点的左右子树都存在,此时又会分成两种情形 1)左节点是当前左子树的最大节点,此时只需要用左节点代替根节点即可 /* * * 10 ======> 6 * / \ / * 6 15 5 15 * / * 5 */ 代码该怎么编写呢? STATUS delete_node_from_tree(TREE_NODE** ppTreeNode

一步一步写算法(之排序二叉树删除-3)

原文:一步一步写算法(之排序二叉树删除-3) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 3 普通节点的删除 3.1 删除的节点没有左子树,也没有右子树 测试用例1: 删除节点6 /* * * 10 ======> 10 * / \ * 6 15 15 * */ static void test8() { TREE_NODE* pTreeNode = NULL; assert(TRUE == insert_node_into_tree