日常学习随笔-用链表的形式实现普通二叉树的新增、查找、遍历(前、中、后序)等基础功能(侧重源码+说明)
一、二叉树
1、二叉树的概念
二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree),其次序不能任意颠倒。
2、性质
(1)若二叉树的层次从0开始,则在二叉树的第i层至多有2^i个结点(i>=0);
(2)高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1)。 (空树的高度为-1);
(3)对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n, 则m = n + 1。
二、二叉树的几种类型
这里就不多介绍了,目前的进度了解还不够深入,后续继续逐步研究,下面提供一个其他博主介绍的网址:
https://www.cnblogs.com/love-yh/p/7423301.html
三、给出一个自定义的二叉树的案例(普通二叉树)
二叉树类:MyTreeDefin.java
1 package com.xfwl.algorithmAnalysis.trees; 2 /** 3 * 二叉树结构分析 4 * @function 日常学习测试 5 * @author 小风微凉 6 * @time 2018-5-20 下午12:28:50 7 */ 8 public class MyTreeDefin<T extends Comparable<? super T>>{ 9 /** 10 * 每棵树都有一个根节点 11 */ 12 private BinaryNode<T> root; 13 /** 14 * 整棵树的节点个数 15 */ 16 private int nodeCount; 17 /** 18 * 二叉树构造器 19 */ 20 public MyTreeDefin(){ 21 this.reset(); 22 } 23 /** 24 * 重置整棵树的结构 25 */ 26 private void reset(){ 27 //二叉树的根节点初始化 28 root=null; 29 //二叉树的节点总数:归0 30 this.nodeCount=0; 31 } 32 /** 33 * 清空整棵二叉树 34 * @return 35 */ 36 public boolean makeEmpty(){ 37 this.reset(); 38 return this.isEmpty(); 39 } 40 /** 41 * 获取树的节点个数 42 * @return 43 */ 44 public int getSize(){ 45 return this.nodeCount; 46 } 47 /** 48 * 判断整棵树是否为空树 49 * @return 50 */ 51 public boolean isEmpty(){ 52 return this.nodeCount==0?true:false; 53 } 54 /** 55 * 判断二叉树中指定节点后面是否包含指定数据 56 * @param target 检索数据target 57 * @param node 指定的节点(包含当前节点) 58 * @return 59 */ 60 public boolean contains(T target,BinaryNode<T> node){ 61 //判空检查 62 if(node==null){ 63 return false; 64 } 65 //先和当前节点的数据比较 66 int compareResult=target.compareTo(node.elem); 67 if(compareResult>0){//进入右子树中继续判断 68 return contains(target,node.right); 69 }else if(compareResult<0){//进入左子树中继续判断 70 return contains(target,node.left); 71 }else{//相等 72 return true; 73 } 74 } 75 /** 76 * 从根节点开始查找整棵树是否包含指定的数据 77 * @param target 检索数据target 78 * @return 79 */ 80 public boolean contains(T target){ 81 return this.contains(target, this.root); 82 } 83 /** 84 * 查找整棵树中的最小数据 85 * 左子树最后一个节点数据 86 * @return 87 */ 88 public T findMin(){ 89 return this.findMin(this.root).elem; 90 } 91 /** 92 * 查找指定树节点下面的最小节点[查找指定节点后面的最小节点] 93 * @param node 指定的节点 94 * @return 95 */ 96 public BinaryNode<T> findMin(BinaryNode<T> node){ 97 //如果节点为空 98 if(node==null){ 99 return null; 100 }else if(node.left==null){//递归基准情况 101 return node; 102 }else{//递归流程 103 return findMin(node.left); 104 } 105 } 106 /** 107 * 查找指定树节点下面的最大节点[最大的数据在右子树的最深叶子节点] 108 * @param node 指定的查找起点节点 109 * @return 110 */ 111 public BinaryNode<T> findMax(BinaryNode<T> node){ 112 //如果节点为空 113 if(node==null){ 114 return null; 115 }else if(node.right==null){//递归基准情况 116 return node; 117 }else{//递归流程 118 return findMax(node.right); 119 } 120 } 121 /** 122 * 查找整棵树中的最大数据 123 * @return 124 */ 125 public T findMax(){ 126 return this.findMax(this.root).elem; 127 } 128 /** 129 * 为二叉树添加新的节点(对外) 130 * @param data 要添加的数据 131 * @return 132 */ 133 public BinaryNode<T> add(T data){ 134 if(this.isEmpty()){ 135 this.nodeCount++; 136 this.root=new BinaryNode<>(data,null,null); 137 return this.root; 138 }else{ 139 this.nodeCount++; 140 return this.add(data, this.root); 141 } 142 } 143 /** 144 * 为二叉树添加新的节点(对内) 145 * @param data 要添加的数据 146 * @param curNode 要添加的节点(递归比较) 147 */ 148 private BinaryNode<T> add(T data,BinaryNode<T> curNode){ 149 //如果节点不存在:递归基准情况 150 if(curNode==null){ 151 return new BinaryNode<>(data,null,null); 152 } 153 //按照:左>根>右 的大小顺序插入二叉树中 154 //比较起点:先和根节点数据比较 155 int compareResult=data.compareTo(curNode.elem); 156 if(compareResult<0){//走左子树 157 System.out.println("左<--"); 158 curNode.left=this.add(data,curNode.left); 159 }else if(compareResult>0){//走右子树 160 System.out.println("-->右"); 161 curNode.right=this.add(data,curNode.right); 162 }else{//如果添加的节点数据和当前比较的根节点相同 163 //不做任何处理,可在此继续扩展 164 } 165 //返回的是根节点 166 return curNode; 167 } 168 /** 169 * 非递归添加树节点 170 * @param data 节点数据 171 * @return 172 */ 173 public void push(T data){ 174 if(this.isEmpty()){//空树结构 175 this.nodeCount++; 176 this.root=new BinaryNode<>(data,null,null); 177 }else{//至少含有一个根节点 178 BinaryNode<T> tmpNode =this.root; 179 System.out.println("--------------------根节点数据:"+tmpNode.elem+",--------------------"); 180 while(true){ 181 System.out.println("while----:tmpNode.elem="+tmpNode.elem+",data="+data); 182 int compareResult=data.compareTo(tmpNode.elem); 183 if(compareResult<0){//走左子树 184 System.out.println("左<--"); 185 if(tmpNode.left==null){ 186 tmpNode.left=new BinaryNode<>(data,null,null); 187 break; 188 } 189 tmpNode=tmpNode.left; 190 }else if(compareResult>0){//走右子树 191 System.out.println("-->右"); 192 if(tmpNode.right==null){ 193 tmpNode.right=new BinaryNode<>(data,null,null); 194 break; 195 } 196 tmpNode=tmpNode.right; 197 }else{//替换当前节点数据(压入相同数据,没改变) 198 //也即不做处理 199 break; 200 } 201 } 202 this.nodeCount++; 203 } 204 } 205 /** 206 * 移除二叉树中根节点后面的指定数据(所在的节点) 207 * @param data 指定的数据 208 */ 209 public BinaryNode<T> remove(T data){ 210 return this.remove(data, this.root); 211 } 212 /** 213 * [删除是最难的:慢慢琢磨-琢磨好了-以后另开一篇] 214 * 移除二叉树中指定节点后面的指定数据(所在的节点) 215 * @param node 指定的节点 216 * @param data 指定的数据 217 */ 218 public BinaryNode<T> remove(T data,BinaryNode<T> node){ 219 //节点判断 220 if(node==null){ 221 return null; 222 } 223 //开始比较 224 int compareResult=data.compareTo(node.elem); 225 if(compareResult==0){//找到基准情况[刚好找到要删除的数据节点] 226 /** 227 * 找到这个节点 228 */ 229 } 230 //....算了,考虑的头比较痛,下次再开一篇专门研究二叉树删除的案例 231 return null; 232 } 233 /** 234 * 打印树结构信息 235 * @param index 遍历的类型 236 * 一般用到的遍历方式有三种: 237 * (1)前序遍历 0 238 * (2)中序遍历 1 239 * (3)后序遍历 2 240 */ 241 public void printTree(int index){ 242 String type=(index==0?"前序":index==1?"中序":"后序")+"遍历"; 243 System.out.println("------------【开始遍历打印·"+type+"】------------"); 244 switch(index){ 245 case 0:preOrder(this.root);break; 246 case 1:inOrder(this.root);break; 247 case 2:postOrder(this.root);break; 248 } 249 System.out.println("------------【打印结束】------------"); 250 } 251 /** 252 * 前序遍历 253 * @param node 遍历的起始节点 254 * 采用递归思想 255 * 对左子节点进行遍历 256 * 对右子节点进行遍历 257 * 递归基值是node是否是null 258 */ 259 private void preOrder(BinaryNode<T> node) 260 { 261 if(node==null){ 262 return; 263 }else{ 264 System.out.print(node.elem+" ");//输出数据节点信息 265 preOrder(node.left); 266 preOrder(node.right); 267 } 268 } 269 /** 270 * 中序遍历 271 * @param node 272 */ 273 private void inOrder(BinaryNode<T> node){ 274 if(node==null){ 275 return; 276 }else{ 277 inOrder(node.left); 278 System.out.print(node.elem+" "); 279 inOrder(node.right); 280 } 281 } 282 /** 283 * 后序遍历 284 * @param node 285 */ 286 private void postOrder(BinaryNode<T> node){ 287 if(node==null){ 288 return; 289 }else{ 290 postOrder(node.left); 291 postOrder(node.right); 292 System.out.print(node.elem+" "); 293 } 294 } 295 /** 296 * 获取指定节点的数据 297 * @param node 298 * @return 299 */ 300 public T getElem(BinaryNode<T> node){ 301 if(node==null){ 302 return null; 303 } 304 return node.elem; 305 } 306 /** 307 * 内置一个树节点类 308 */ 309 private static class BinaryNode<T>{ 310 /** 311 * 树节点存放数据域 312 */ 313 T elem; 314 /** 315 * 左子树节点域 316 */ 317 BinaryNode<T> left; 318 /** 319 * 右子树节点域 320 */ 321 BinaryNode<T> right; 322 /** 323 * 构造器 324 */ 325 public BinaryNode(T elem,BinaryNode<T> left,BinaryNode<T> right){ 326 this.elem=elem; 327 this.left=left; 328 this.right=right; 329 } 330 } 331 } 332
测试类:Test.java
1 package com.xfwl.algorithmAnalysis.trees; 2 3 public class Test { 4 /** 5 * @param args 6 */ 7 public static void main(String[] args) { 8 //创建一棵树 9 MyTreeDefin<Integer> tree=new MyTreeDefin<>(); 10 //压入数据 11 tree.push(5); 12 tree.push(2); 13 tree.push(4); 14 tree.push(8); 15 tree.push(7); 16 tree.push(10); 17 tree.push(3); 18 tree.push(9); 19 tree.push(6); 20 tree.push(1); 21 System.out.println(tree.getSize()); 22 //开始遍历显示 23 tree.printTree(0); 24 tree.printTree(1); 25 tree.printTree(2); 26 //删除 27 } 28 }
测试结果:
--------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=2 左<-- --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=4 左<-- while----:tmpNode.elem=2,data=4 -->右 --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=8 -->右 --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=7 -->右 while----:tmpNode.elem=8,data=7 左<-- --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=10 -->右 while----:tmpNode.elem=8,data=10 -->右 --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=3 左<-- while----:tmpNode.elem=2,data=3 -->右 while----:tmpNode.elem=4,data=3 左<-- --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=9 -->右 while----:tmpNode.elem=8,data=9 -->右 while----:tmpNode.elem=10,data=9 左<-- --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=6 -->右 while----:tmpNode.elem=8,data=6 左<-- while----:tmpNode.elem=7,data=6 左<-- --------------------根节点数据:5,-------------------- while----:tmpNode.elem=5,data=1 左<-- while----:tmpNode.elem=2,data=1 左<-- 10 ------------【开始遍历打印·前序遍历】------------ 5 2 1 4 3 8 7 6 10 9 ------------【打印结束】------------ ------------【开始遍历打印·中序遍历】------------ 1 2 3 4 5 6 7 8 9 10 ------------【打印结束】------------ ------------【开始遍历打印·后序遍历】------------ 1 3 4 2 6 7 9 10 8 5 ------------【打印结束】------------
四、总结一下
最近比较忙,学习进度明显感到有点延迟了,还是得抓紧时间啊!上面的测试例子并没有写“二叉树的删除”功能,删除也比较麻烦,分为很多种情形,需要仔细分析,此篇就不在分析说明了,以后再慢慢研究,要学的东西还很多,慢慢来吧!
研究技术需要静下心来,一点一点地深究.......