1 package com.tree; 2 3 /* 4 * 为什么需要树这种数据结构 5 * 1)数组存储方式的分析: 6 * 优点:通过下标方式访问元素,速度快,对于有序数组,还可以使用二分查找提高检索速度 7 * 缺点:如果检索具体某个值,或者插入值(按一定顺序)会整体一定,效率非常低 8 * 2)链式存储方式的分析 9 * 优点:在一定程度上渡数组存储方式有优化(比如:插入一个数值节点,只需要将插入节点,链接到链表中 10 * 即可,删除效率也很好 11 * 缺点:在进行检索时,效率仍然较低,比如(检索某个值,需要从头节点开始遍历); 12 * 3)树存储方式的分析 13 * 能提高数据存储、读取的效率,比如利用二叉排序树,既可以保证数据的检索速度, 14 * 同时也可以保证数据的插入、删除、修改速度 15 * 下面以[7,3,10,1,5,9,12]用二叉排序树来存储数据,如下,简单演示增删改查效率如何都可以提高 16 * 7 17 * / \ 18 * 3 10 19 * / \ / \ 20 * 1 5 9 12 21 * 分析:1、假如现在需要检索12 22 * 1)首先12跟7比较,则,12比7大,所以12应该在右子树 23 * 2)12继续与10比较,发现比12还大,所以在10的右字树 24 * 3)12跟12比较,找到 25 * 2、插入一个13 26 * 1)首先跟7比较,发现比7大,所以应该在7的右字树上 27 * 2)继续跟10比较,发现比10也大,所以应该在10的右字树上 28 * 3)继续跟12比较,发现比12还大,所以应该在12的右字树上 29 * 4)12的右子数节点为空,所以直接作为12的右子节点插入即可 30 * 3、删除的如果是中间节点,则需要做一点点处理,这里暂时不阐述 31 * 32 * 树的常用术语: 33 * 1、节点:节点对象 34 * 2、根节点: 35 * 3、父节点 36 * 4、子节点 37 * 5、叶子节点:没有子节点的节点 38 * 6、节点的权:节点值 39 * 7、路径:从root节点找到该节点的路线 40 * 8、层 41 * 9、子树 42 * 10、树的高度:最大层数 43 * 11、森林:多棵子树构成森林 44 * 45 * 二叉树: 46 * 1、树有很多种,每个节点最多只能有两个子节点的一种形式成为二叉树 47 * 2、二叉树的子节点分为左子节点和右子节点 48 * 3、如果该二叉树的所有叶子节点都在最后一层,并且节点总数=2**n-1,n为层数,我们称之为满二叉树 49 * 4、如果该二叉树的所有叶子节点都在最后一层,或者倒数第二层,而且最后一层的叶子节点在左边连续, 50 * 在倒数第二层的叶子节点在右边连续,我们称之为完全二叉树 51 * 52 * 二叉树遍历的方式 53 * 1、前序:先输出父节点,再遍历左子树和右字树 54 * 2、中序:先遍历左子树,再输出父节点,再遍历右字树 55 * 3、后序:先遍历左子树,再遍历右字树,最后输出父节点 56 * 小结:看输出父节点的顺序,就确定是前序、中序还是后序 57 * 58 * 二叉树遍历思路 59 * 1.创建一棵二叉树,比如 60 * A 61 * / \ 62 * B C 63 * / \ / \ 64 * D E F G 65 * 2、前序遍历 66 * 1)先输出当前节点 67 * 2)如果左子节点不为空,则递归继续前序遍历 68 * 3)如果右字节点不为空,则递归继续前序遍历 69 * 3、中序遍历: 70 * 1)如果当前节点的左子节点不为空,则递归继续中序遍历 71 * 2)输出当前节点 72 * 3)如果当前节点的右子节点不为空,则递归继续中序遍历 73 * 4、后序遍历: 74 * 1)如果当前节点的左子节点不为空,则递归继续后序遍历 75 * 2)如果当前节点的右子节点不为空,则迪拜继续后序遍历 76 * 3)输出当前节点 77 * 78 * 二叉树-查找指定节点 79 * 要求: 80 * 1)请编写前序查找、中序查找和后序查找的方法 81 * 2)并分别利用三种查找方式,查找heroNo=5的节点 82 * 3)并分析各种查找方式,分别比较了多少次 83 * 思路: 84 * 前序查找思路: 85 * 1)先判断当前节点的no是否等于要查找的 86 * 2)如果相等,则返回当前节点 87 * 3)如果不等,则判断当前节点的左子节点是否为空,如果不为空,则递归前序查找 88 * 4)如果左递归程序查找,找到节点,则返回,否则继续判断,当前的节点的右子节点是否为空 89 * 如果不为空,继续向右递归前序查找 90 * 中序查找思路: 91 * 1)判断当前节点的左子节点是否为空,如果不为空,则递归中序查找 92 * 2)如果找到,则返回,如果没有找到,就和当前节点比较,如果是则返回当前节点, 93 * 否则继续进行递归的中序查找 94 * 3)如果右递归中序查找,找到则返回,否则返回null 95 * 后序查找思路: 96 * 1)判断当前节点的左子节点是否为空,如果不为空,则递归中序查找 97 * 2)如果找到,就返回,如果没有找到,则判断当前节点的右子节点是否为空, 98 * 如果不为空,则右递归进行后序查找,如果找到就返回 99 * 3)如果没找到就和当前节点比较,如果是则返回,否则返回null 100 * 101 * 二叉树-删除节点 102 * 要求: 103 * 1、如果删除的节点是叶子节点,则删除该节点 104 * 2、如果删除的节点时非叶子节点,则删除该子树(先简单处理,深入处理后序再继续) 105 * 3、测试、删除掉5号叶子节点和2号子树 106 * 思路: 107 * 0、首先:考虑如果树是空树root,如果只有一个root节点,则等价于将二叉树置空 108 * 1、因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否需要删除, 109 * 而不能取判断当前节点是否需要删除 110 * 2、如果当前节点的左子树不为空,并且左子节点的编号就是需要删除的节点, 111 * 就将this.left置空,并且返回,结束递归删除 112 * 3、如果当期节点的右子节点不为空,并且右子节点就是要删除的节点,就将 113 * this.right置空,并且返回,并且结束递归删除 114 * 4、如果第2和第3步都没有删除,那么就需要向左字树递归删除 115 * 5、如果第4步也没有删除,则应当向右字树进行递归删除 116 */ 117 118 class HeroNode 119 { 120 private int no; 121 private String name; 122 private HeroNode left; 123 private HeroNode right; 124 125 HeroNode(int no,String name) 126 { 127 this.no=no; 128 this.name=name; 129 } 130 131 public int getNo() { 132 return no; 133 } 134 135 public void setNo(int no) { 136 this.no = no; 137 } 138 139 public String getName() { 140 return name; 141 } 142 143 public void setName(String name) { 144 this.name = name; 145 } 146 147 public HeroNode getLeft() { 148 return left; 149 } 150 151 public void setLeft(HeroNode left) { 152 this.left = left; 153 } 154 155 public HeroNode getRight() { 156 return right; 157 } 158 159 public void setRight(HeroNode right) { 160 this.right = right; 161 } 162 163 @Override 164 public String toString() { 165 return "HeroNode [no=" + no + ", name=" + name + "]"; 166 } 167 168 //前序遍历的方法 169 public void preOrder() 170 { 171 System.out.println(this);//先输出父节点 172 //向左字树递归 173 if(this.left!=null) 174 { 175 this.left.preOrder(); 176 } 177 178 //向右字树递归 179 if(this.right!=null) 180 { 181 this.right.preOrder(); 182 } 183 } 184 //中序遍历的方法 185 public void infixOrder() 186 { 187 //向左子树递归中序遍历 188 if(this.left!=null) 189 { 190 this.left.infixOrder(); 191 } 192 //输出父节点 193 System.out.println(this); 194 //向右字树递归中序遍历 195 if(this.right!=null) 196 { 197 this.right.infixOrder(); 198 } 199 } 200 //后序遍历的方法 201 public void postOrder() 202 { 203 //向左子树递归后序遍历 204 if(this.left!=null) 205 { 206 this.left.postOrder(); 207 } 208 //向右字树递归后序遍历 209 if(this.right!=null) 210 { 211 this.right.postOrder(); 212 } 213 //输出当前父节点 214 System.out.println(this); 215 } 216 217 /** 218 * 前序遍历查找 219 * @param no 查找no 220 * @return 如果找到就返回该Node,如果没找到就返回null 221 */ 222 public HeroNode preOrderSearch(int no) { 223 System.out.println("进入前序遍历查找......"); 224 if(this.no == no) 225 { 226 227 return this; 228 } 229 HeroNode resNode=null; 230 if(this.left!=null) 231 { 232 resNode=this.left.preOrderSearch(no); 233 } 234 if(resNode !=null) 235 { 236 //说明左子树找到了 237 return resNode; 238 } 239 if(this.right!=null) 240 { 241 resNode=this.right.preOrderSearch(no); 242 } 243 return resNode; 244 } 245 /** 246 * 中序遍历查找 247 * @param no 查找no 248 * @return 如果找到就返回该Node,如果没找到就返回null 249 */ 250 public HeroNode infixOrderSearch(int no) 251 { 252 253 HeroNode resNode=null; 254 if(this.left!=null) 255 { 256 resNode=this.left.infixOrderSearch(no); 257 } 258 if(resNode!=null) 259 { 260 return resNode; 261 } 262 System.out.println("进入中序遍历查找......"); 263 if(this.no==no) 264 { 265 266 return this; 267 } 268 if(this.right!=null) 269 { 270 resNode=this.right.infixOrderSearch(no); 271 } 272 return resNode; 273 } 274 /** 275 * 后序遍历查找 276 * @param no 查找no 277 * @return 如果找到就返回该Node,如果没找到就返回null 278 */ 279 public HeroNode postOrderSearch(int no) 280 { 281 282 HeroNode resNode=null; 283 if(this.left!=null) 284 { 285 resNode=this.left.postOrderSearch(no); 286 } 287 if(resNode!=null) 288 { 289 return resNode; 290 } 291 if(this.right!=null) 292 { 293 resNode=this.right.postOrderSearch(no); 294 } 295 if(resNode!=null) 296 { 297 return resNode; 298 } 299 System.out.println("进入后序遍历查找......"); 300 if(this.no==no) 301 { 302 303 return this; 304 } 305 else 306 { 307 return null; 308 } 309 } 310 311 public void delNode(int no) 312 { 313 /* 314 * 思路: 315 * 1、因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否需要删除, 316 * 而不能取判断当前节点是否需要删除 317 * 2、如果当前节点的左子树不为空,并且左子节点的编号就是需要删除的节点, 318 * 就将this.left置空,并且返回,结束递归删除 319 * 3、如果当期节点的右子节点不为空,并且右子节点就是要删除的节点,就将 320 * this.right置空,并且返回,并且结束递归删除 321 * 4、如果第2和第3步都没有删除,那么就需要向左字树递归删除 322 * 5、如果第4步也没有删除,则应当向右字树进行递归删除 323 */ 324 325 if(this.left!=null && this.left.no==no) 326 { 327 this.left=null; 328 return; 329 } 330 if(this.right!=null && this.right.no == no) 331 { 332 this.right=null; 333 return; 334 } 335 if(this.left!=null) 336 { 337 this.left.delNode(no); 338 } 339 if(this.right!=null) 340 { 341 this.right.delNode(no); 342 } 343 } 344 } 345 346 //定义一个BinaryTree 347 class BinaryTree 348 { 349 private HeroNode root; 350 351 public HeroNode getRoot() { 352 return root; 353 } 354 355 public void setRoot(HeroNode root) { 356 this.root = root; 357 } 358 359 public void preOrder() 360 { 361 if(this.root!=null) 362 { 363 this.root.preOrder(); 364 } 365 else 366 { 367 System.out.println("当前二叉树为空,无法遍历"); 368 } 369 } 370 371 public void infixOrder() 372 { 373 if(this.root!=null) 374 { 375 this.root.infixOrder(); 376 } 377 else 378 { 379 System.out.println("当前二叉树为空,无法遍历"); 380 } 381 } 382 383 public void postOrder() 384 { 385 if(this.root!=null) 386 { 387 this.root.postOrder(); 388 } 389 else 390 { 391 System.out.println("当前二叉树为空,无法遍历"); 392 } 393 } 394 395 public HeroNode preOrderSearch(int no) 396 { 397 if(this.root!=null) 398 { 399 return this.root.preOrderSearch(no); 400 } 401 else 402 { 403 return null; 404 } 405 } 406 407 public HeroNode infixOrderSearch(int no) 408 { 409 if(this.root!=null) 410 { 411 return this.root.infixOrderSearch(no); 412 } 413 else 414 { 415 return null; 416 } 417 } 418 419 public HeroNode postOrderSearch(int no) 420 { 421 if(this.root!=null) 422 { 423 return this.root.postOrderSearch(no); 424 } 425 else 426 { 427 return null; 428 } 429 } 430 public void delNode(int no) 431 { 432 if(this.root!=null) 433 { 434 if(this.root.getNo()==no) 435 { 436 this.root=null; 437 } 438 else 439 { 440 this.root.delNode(no); 441 } 442 } 443 else 444 { 445 System.out.println("这是一棵空树,不能删除......"); 446 } 447 } 448 } 449 public class BinaryTreeDemo { 450 451 public static void main(String[] args) { 452 // TODO Auto-generated method stub 453 BinaryTree bt=new BinaryTree(); 454 HeroNode root=new HeroNode(1,"宋江"); 455 HeroNode n2=new HeroNode(2,"无用"); 456 HeroNode n3=new HeroNode(3,"卢俊义"); 457 HeroNode n4=new HeroNode(4,"林冲"); 458 HeroNode n5=new HeroNode(5,"关胜"); 459 //说明:先手动创建二叉树,后面学习递归的方法创建二叉树 460 root.setLeft(n2); 461 root.setRight(n3); 462 n3.setRight(n4); 463 n3.setLeft(n5); 464 bt.setRoot(root); 465 466 System.out.println("前序遍历:"); 467 bt.preOrder(); 468 System.out.println("中序遍历:"); 469 bt.infixOrder(); 470 System.out.println("后序遍历:"); 471 bt.postOrder(); 472 473 System.out.println("前序查找:..."); 474 System.out.println("前序查找结果5: "+bt.preOrderSearch(5)); 475 System.out.println("中序查找结果5: "+bt.infixOrderSearch(5)); 476 System.out.println("中序查找结果5: "+bt.postOrderSearch(5)); 477 478 System.out.println("前序查找结果15: "+bt.preOrderSearch(15)); 479 System.out.println("中序查找结果15: "+bt.infixOrderSearch(15)); 480 System.out.println("中序查找结果15: "+bt.postOrderSearch(15)); 481 482 System.out.println("测试一下delNode的方法......"); 483 System.out.println("删除前的bt:"); 484 bt.preOrder(); 485 bt.delNode(5); 486 System.out.println("删除后的bt:"); 487 bt.preOrder(); 488 489 System.out.println("删除前的bt:"); 490 bt.preOrder(); 491 bt.delNode(3); 492 System.out.println("删除后的bt:"); 493 bt.preOrder(); 494 } 495 }
执行结果为:
前序遍历: HeroNode [no=1, name=宋江] HeroNode [no=2, name=无用] HeroNode [no=3, name=卢俊义] HeroNode [no=5, name=关胜] HeroNode [no=4, name=林冲] 中序遍历: HeroNode [no=2, name=无用] HeroNode [no=1, name=宋江] HeroNode [no=5, name=关胜] HeroNode [no=3, name=卢俊义] HeroNode [no=4, name=林冲] 后序遍历: HeroNode [no=2, name=无用] HeroNode [no=5, name=关胜] HeroNode [no=4, name=林冲] HeroNode [no=3, name=卢俊义] HeroNode [no=1, name=宋江] 前序查找:... 进入前序遍历查找...... 进入前序遍历查找...... 进入前序遍历查找...... 进入前序遍历查找...... 前序查找结果5: HeroNode [no=5, name=关胜] 进入中序遍历查找...... 进入中序遍历查找...... 进入中序遍历查找...... 中序查找结果5: HeroNode [no=5, name=关胜] 进入后序遍历查找...... 进入后序遍历查找...... 中序查找结果5: HeroNode [no=5, name=关胜] 进入前序遍历查找...... 进入前序遍历查找...... 进入前序遍历查找...... 进入前序遍历查找...... 进入前序遍历查找...... 前序查找结果15: null 进入中序遍历查找...... 进入中序遍历查找...... 进入中序遍历查找...... 进入中序遍历查找...... 进入中序遍历查找...... 中序查找结果15: null 进入后序遍历查找...... 进入后序遍历查找...... 进入后序遍历查找...... 进入后序遍历查找...... 进入后序遍历查找...... 中序查找结果15: null 测试一下delNode的方法...... 删除前的bt: HeroNode [no=1, name=宋江] HeroNode [no=2, name=无用] HeroNode [no=3, name=卢俊义] HeroNode [no=5, name=关胜] HeroNode [no=4, name=林冲] 删除后的bt: HeroNode [no=1, name=宋江] HeroNode [no=2, name=无用] HeroNode [no=3, name=卢俊义] HeroNode [no=4, name=林冲] 删除前的bt: HeroNode [no=1, name=宋江] HeroNode [no=2, name=无用] HeroNode [no=3, name=卢俊义] HeroNode [no=4, name=林冲] 删除后的bt: HeroNode [no=1, name=宋江] HeroNode [no=2, name=无用]