摘要: 一個二叉樹的Java實現。可以學習廣義表達式及二叉樹的遞歸及非遞歸處理技巧。
難度:初級。
為了克服對樹結構編程的畏懼感和神秘感,下定決心將二叉樹的大部分操作實現一遍,並希望能夠掌握二叉樹編程的一些常用技術和技巧。
[1] 數據結構和表示: 二叉樹的輸入輸出格式采用廣義表表達式形式,內部表示采用左孩子右孩子的鏈式存儲。
[2] 已經實現的操作有:
A. 根據二叉樹的廣義表表達式來創建二叉樹(含表達式合法性檢測);
B. 根據二叉樹的前序和中序遍歷列表來創建二叉樹;
C. 根據二叉樹的中序和后序遍歷列表來創建二叉樹;
D. 二叉樹的“左孩子右孩子”鏈式存儲轉化為廣義表表達式;
E. 二叉樹的前序、中序、后序的遞歸和非遞歸遍歷;層序遍歷;結點關聯列表;
F. 求解二叉樹的高度、結點總數、葉子結點總數、所有的葉子結點列表;根結點到所有葉子結點的路徑;最長路徑;
G. 二叉樹復制,二叉樹全等性比較,二叉樹交換(將二叉樹的所有結點的左右子樹互換)
[3] 尚待實現的操作:
A. 求解二叉樹中所有最長(短)路徑
E. 其它
[4] 二叉樹 Java 實現代碼:
1 /** 2 * BinaryTree: 實現二叉樹,可以根據給定的廣義表表達式、前序及中序遍歷列表、中序及后序遍歷列表創建二叉樹; 3 * 二叉樹的前序、中序、后序的遞歸和非遞歸遍歷,層序遍歷;求解二叉樹的一些特性 4 * 5 * @author shuqin1984 2011-3-19 6 * 7 */ 8 package datastructure.tree; 9 import java.util.ArrayList; 10 import java.util.Iterator; 11 import java.util.LinkedList; 12 import java.util.List; 13 import java.util.regex.Matcher; 14 import java.util.regex.Pattern; 15 16 public class BinaryTree { 17 18 /** 二叉樹的廣義表表示 */ 19 private String expression; 20 /** 樹的根結點 */ 21 private TreeNode root; 22 23 /** 24 * 用於檢測二叉樹廣義表表達式合法性的編譯了的正則表達式: 25 * 合法的廣義表表達式可以是: 26 * [1] 基本表達式: A,A(,), A(B,), A(,B), A(B,C) 27 * [2] 上述的 A,B,C 可以是 [1] 中的基本表達式 28 * [3] A,B,C 可允許的取值范圍由應用決定,這里僅允許是 大小寫字母,數字,+-/*% 29 * [4] 表達式中不含任何空格符,因此,在檢查表達式之前,必須確保表達式中不含空格符 30 * 31 */ 32 private static String permittedChars = "[a-zA-Z0-9//+//-//*/////%]"; 33 private static String basicUnit = "[a-zA-Z0-9//+//-//*/////%//(//),]"; 34 private static Pattern basicPattern = Pattern.compile("" + "|" + permittedChars + "|" + permittedChars + "//(" + permittedChars + "?," + permittedChars + "?//)?"); 35 private static Pattern extendPattern = Pattern.compile(permittedChars + "//(" + basicUnit + "*," + basicUnit + "*//)"); 36 37 /** 38 * 構造器 39 * @param root 樹的根結點 40 */ 41 public BinaryTree(TreeNode root) { 42 this.root = root; 43 } 44 45 /** 46 * 構造器 47 * @param expression 二叉樹的廣義表表達式 48 */ 49 private BinaryTree(String expression) { 50 this.expression = expression; 51 } 52 /** 53 * 樹結點實現 54 */ 55 private class TreeNode { 56 57 private char ch; 58 private TreeNode rchild; 59 private TreeNode lchild; 60 61 public TreeNode(char ch, TreeNode rchild, TreeNode lchild) { 62 this.ch = ch; 63 this.rchild = rchild; 64 this.lchild = lchild; 65 } 66 public char getCh() { 67 return ch; 68 } 69 public TreeNode getRchild() { 70 return rchild; 71 } 72 public void setRchild(TreeNode rchild) { 73 this.rchild = rchild; 74 } 75 public TreeNode getLchild() { 76 return lchild; 77 } 78 public void setLchild(TreeNode lchild) { 79 this.lchild = lchild; 80 } 81 82 public String toString() 83 { 84 return "" + getCh(); 85 } 86 } 87 88 /** 89 * 結點關聯類 90 */ 91 private class NodeLink { 92 93 private TreeNode node1; 94 private TreeNode node2; 95 96 public NodeLink(TreeNode node1, TreeNode node2) { 97 this.node1 = node1; 98 this.node2 = node2; 99 } 100 101 public String toString() { 102 103 return "(" + node1.getCh() + "," + node2.getCh() + ")"; 104 } 105 106 } 107 108 /** 109 * 【設置/獲取】屬性 110 */ 111 public TreeNode getRoot() { 112 return root; 113 } 114 public void setRoot(TreeNode root) { 115 this.root = root; 116 } 117 118 119 /** 120 * getPreOrderList: 獲得樹的先序遍歷列表 121 * @param flag 是否采用遞歸遍歷的標記;若 flag = true, 采用遞歸遍歷;否則,采用非遞歸遍歷 122 * @return 二叉樹的先序遍歷列表 123 */ 124 125 public List<TreeNode> getPreOrderList(boolean flag) { 126 127 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 128 if (flag == true) { 129 nodelist = preOrderTraverse(getRoot(), nodelist); 130 } 131 else { 132 nodelist = preOrderTraverseIter(getRoot()); 133 } 134 return nodelist; 135 } 136 137 /** 138 * getInOrderList: 獲得樹的中序遍歷列表 139 * @param flag 是否采用遞歸遍歷的標記;若 flag = true, 采用遞歸遍歷;否則,采用非遞歸遍歷 140 * @return 獲得樹的中序遍歷列表 141 */ 142 143 public List<TreeNode> getInOrderList(boolean flag) { 144 145 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 146 if (flag == true) { 147 nodelist = inOrderTraverse(getRoot(), nodelist); 148 } 149 else { 150 nodelist = inOrderTraverseIter(getRoot()); 151 } 152 return nodelist; 153 } 154 155 /** 156 * getPostOrderList: 獲得樹的后序遍歷列表 157 * @param flag 是否采用遞歸遍歷的標記;若 flag = true, 采用遞歸遍歷;否則,采用非遞歸遍歷 158 * @return 獲得樹的后序遍歷列表 159 */ 160 161 public List<TreeNode> getPostOrderList(boolean flag) { 162 163 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 164 if (flag == true) { 165 nodelist = postOrderTraverse(getRoot(), nodelist); 166 } 167 else { 168 nodelist = postOrderTraverseIter(getRoot()); 169 } 170 return nodelist; 171 } 172 173 /** 174 * 獲得樹的層序遍歷列表 175 * 176 * @return 獲得樹的層序遍歷列表 177 */ 178 179 public List<TreeNode> getFloorOrderList() { 180 181 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 182 nodelist = floorOrderTraverse(getRoot()); 183 return nodelist; 184 } 185 186 /** 187 * createBinaryTree: 根據樹的廣義表表示構造二叉樹 188 * @throws Exception 189 */ 190 public static BinaryTree createBinaryTree(String expression) throws Exception 191 { 192 BinaryTree bt = new BinaryTree(trimSpace(expression)); 193 bt.createBinaryTree(); 194 return bt; 195 } 196 197 /** 198 * createBinaryTree: 根據二叉樹的廣義表表達式來創建二叉樹 199 * @exception 若二叉樹的廣義表表達式不合法,則拋出異常 200 */ 201 private void createBinaryTree() throws Exception { 202 203 // 檢查傳入的二叉樹廣義表示法是否合法有效 204 if (!checkValid(expression)) 205 throw new Exception("廣義表表達式不合法,無法創建二叉樹!"); 206 207 // 使用 LinkedList 來執行棧的功能 208 LinkedList<TreeNode> stack = new LinkedList<TreeNode>(); 209 TreeNode newnode = null; 210 int flag = 0; // flag = 0: 創建左子樹 | flag = 1: 創建右子樹 211 212 for (char ch: expression.toCharArray()) { 213 switch (ch) { 214 215 // 遇到 "(" 時,表示該結點可能有孩子結點,將該父結點壓入棧中 216 case '(': 217 stack.push(newnode); 218 flag = 0; 219 break; 220 221 // 遇到 ")" 時,表示已經掃描完父結點的右孩子結點的值,彈出該父結點 222 case ')': 223 stack.pop(); 224 break; 225 226 // 遇到 "," 時, 表示將要掃描父結點的右孩子結點的值。 227 case ',': 228 flag = 1; 229 break; 230 231 // 遇到結點的值,將其加入二叉樹中 232 default: 233 234 newnode = new TreeNode(ch, null, null); 235 if (root == null) { 236 root = newnode; 237 } 238 else { 239 if (flag == 0) { 240 TreeNode topnode = stack.peek(); 241 topnode.setLchild(newnode); 242 } 243 else 244 { 245 TreeNode topnode = stack.peek(); 246 topnode.setRchild(newnode); 247 } 248 } 249 break; 250 } 251 } 252 } 253 254 /** 255 * checkValid: 判斷給定二叉樹的廣義表表示是否合法有效 256 * 257 * @param expression 給定二叉樹的廣義表表示【字符串形式】 258 * @return 如果給定的二叉樹廣義表表示合法有效,返回true; 否則,返回 false 259 * 260 */ 261 private static boolean checkValid(String expression) 262 { 263 Matcher m = null; 264 if (basicPattern.matcher(expression).matches()) 265 return true; 266 else if ((m = extendPattern.matcher(expression)).matches()) { 267 int index = separatorIndex(expression); 268 if (index == -1) { // 不存在能夠分割二叉樹廣義表達式的左右子樹表達式的逗號 269 return false; 270 } 271 String rightEx = ""; 272 String leftEx = ""; 273 if (index > 2) { 274 leftEx = expression.substring(2, index); // 左子樹的廣義表達式 275 } 276 if (index < expression.length()-2) { 277 rightEx = expression.substring(index+1, expression.length()-1); // 右子樹的廣義表達式 278 } 279 return checkValid(leftEx) && checkValid(rightEx); 280 } 281 else { 282 return false; 283 } 284 } 285 286 287 /** 288 * getGeneralList: 獲取該二叉樹的廣義表表示(字符串表示) 289 */ 290 public String getGeneralListString() 291 { 292 StringBuilder sb = new StringBuilder(""); 293 if (expression == null) { 294 createGeneralList(root, sb); 295 return sb.toString(); 296 } 297 return expression; 298 } 299 300 /** 301 * getGeneralList: 根據給定二叉樹生成其廣義表表示(字符串形式) 302 * @param root 樹的根結點 303 * @return 樹的廣義表表示【字符串形式】 304 */ 305 private void createGeneralList(TreeNode root, StringBuilder sb) { 306 307 if (root != null) { 308 309 sb.append(root.getCh()); 310 if (root.getLchild() != null || root.getRchild() != null) { 311 sb.append('('); 312 if (root.getLchild() != null) { 313 createGeneralList(root.getLchild(), sb); 314 } 315 sb.append(','); 316 if (root.getRchild() != null) { 317 createGeneralList(root.getRchild(), sb); 318 } 319 sb.append(')'); 320 } 321 } 322 } 323 324 /** 325 * size: 獲取二叉樹的結點總數 326 * 327 * @param root 樹的根結點 328 * @return 樹的結點總數 329 * 330 */ 331 public int size(TreeNode root) { 332 333 if (root == null) 334 return 0; 335 else { 336 return size(root.getLchild()) + size(root.getRchild()) + 1; 337 } 338 } 339 340 /** 341 * leafCounter: 獲取二叉樹的葉子結點數目 342 * 343 * @param root 樹的根結點 344 * @return 樹的葉子結點數目 345 * 346 */ 347 public int leafCounter(TreeNode root) { 348 if (root == null) 349 return 0; 350 else { 351 if (root.getLchild() == null && root.getRchild() == null) 352 return 1; 353 else 354 return leafCounter(root.getLchild()) + leafCounter(root.getRchild()); 355 } 356 } 357 358 /** 359 * getLeafNodes : 獲取該二叉樹的所有葉子結點 360 */ 361 public List<TreeNode> getLeafNodes() 362 { 363 List<TreeNode> leaflist = new ArrayList<TreeNode>(); 364 getLeafNodes(getRoot(), leaflist); 365 return leaflist; 366 } 367 368 /** 369 * printLeafPaths : 打印該二叉樹的所有葉子結點到根的路徑 370 */ 371 public void printLeafPaths() 372 { 373 List<TreeNode> leafPath = new ArrayList<TreeNode>(); 374 buildLeafPaths(root, leafPath); 375 } 376 377 /** 378 * buildLeafPaths : 遞歸求解給定二叉樹的所有葉子結點到根的路徑 379 * @param root 給定二叉樹的根結點 380 * @param path 存放某個葉子結點到根的路徑 381 */ 382 public void buildLeafPaths(TreeNode root, List<TreeNode> path) 383 { 384 if (root != null) { 385 path.add(root); // 將從根結點到葉子結點的路徑上的結點保存起來 386 if (root.getLchild() == null && root.getRchild() == null) { // 到達葉子結點,完成一條路徑,並可對其處理 387 processPath(path); 388 } 389 else { 390 buildLeafPaths(root.getLchild(), path); 391 if (root.getLchild() != null) { 392 path.remove(path.size()-1); // 回溯,從左子樹到右子樹,刪除前一條路徑上的葉子結點 393 } 394 buildLeafPaths(root.getRchild(), path); 395 if (root.getRchild() != null) { 396 path.remove(path.size()-1); 397 } 398 } 399 } 400 } 401 402 /** 403 * processPath : 處理從某葉子結點到根結點的路徑的操作 404 */ 405 private void processPath(List<TreeNode> path) 406 { 407 System.out.println(listToString(path)); 408 } 409 410 /** 411 * getLeafNodes: 遞歸求解給定二叉樹的所有葉子結點 412 * @param root 給定二叉樹的根結點 413 * @param leaflist 給定二叉樹的所有葉子結點 414 */ 415 private void getLeafNodes(TreeNode root, List<TreeNode> leaflist) 416 { 417 if (root != null) { 418 if (root.getLchild() == null && root.getRchild() == null) { 419 leaflist.add(root); 420 return ; 421 } 422 getLeafNodes(root.getLchild(), leaflist); 423 getLeafNodes(root.getRchild(), leaflist); 424 } 425 } 426 427 428 /** 429 * height: 獲取二叉樹的高度 430 * 431 * @param root 樹的根結點 432 * @return 樹的高度 433 * 434 */ 435 436 public int height(TreeNode root) 437 { 438 if (root == null) 439 return 0; 440 else 441 return 1 + Math.max(height(root.getLchild()), height(root.getRchild())); 442 } 443 444 /** 445 * getNodelinkList: 獲取該二叉樹的結點關聯列表 446 * @return 樹的結點關聯列表 447 */ 448 public List<NodeLink> getNodelinkList() { 449 450 List<NodeLink> linklist = new ArrayList<NodeLink>(); 451 createNodelinkList(getRoot(), linklist); 452 return linklist; 453 454 } 455 456 /** 457 * createNodelinkList: 遞歸求解給定二叉樹的結點關聯列表表示 458 * @param root 給定二叉樹的根結點 459 * @param linklist 存放給定二叉樹的結點關聯對象 460 */ 461 private void createNodelinkList(TreeNode root, List<NodeLink> linklist) { 462 463 if (root != null) { 464 if (root.getLchild() != null) { 465 NodeLink nodelink = new NodeLink(root, root.getLchild()); 466 linklist.add(nodelink); 467 createNodelinkList(root.getLchild(), linklist); 468 } 469 if (root.getRchild() != null) { 470 NodeLink nodelink = new NodeLink(root, root.getRchild()); 471 linklist.add(nodelink); 472 createNodelinkList(root.getRchild(), linklist); 473 } 474 } 475 } 476 477 /** 478 * preOrderTraverse: 二叉樹的遞歸先序遍歷 479 * 480 */ 481 private List<TreeNode> preOrderTraverse(TreeNode root, List<TreeNode> nodelist) { 482 483 if (root != null) { 484 nodelist.add(root); 485 preOrderTraverse(root.getLchild(), nodelist); 486 preOrderTraverse(root.getRchild(), nodelist); 487 } 488 489 return nodelist; 490 } 491 492 /** 493 * preOrderTraverseIter: 二叉樹的非遞歸先序遍歷 494 */ 495 private List<TreeNode> preOrderTraverseIter(TreeNode root) { 496 LinkedList<TreeNode> stack = new LinkedList<TreeNode>(); 497 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 498 TreeNode pNode = root; 499 for (;;) { 500 while (pNode != null) { 501 nodelist.add(pNode); // 訪問根結點 502 stack.push(pNode); // 根結點入棧 503 pNode = pNode.getLchild(); // 訪問左子樹 504 } 505 pNode = stack.pop(); 506 pNode = pNode.getRchild(); // 訪問右子樹 507 if (pNode == null && stack.isEmpty()) { break; } 508 } 509 return nodelist; 510 } 511 512 /** 513 * inOrderTraverse: 二叉樹的遞歸中序遍歷 514 * 515 */ 516 private List<TreeNode> inOrderTraverse(TreeNode root, List<TreeNode> nodelist) { 517 518 if (root != null) { 519 inOrderTraverse(root.getLchild(), nodelist); 520 nodelist.add(root); 521 inOrderTraverse(root.getRchild(), nodelist); 522 } 523 524 return nodelist; 525 } 526 527 /** 528 * inOrderTraverseIter: 二叉樹的非遞歸中序遍歷 529 */ 530 531 private List<TreeNode> inOrderTraverseIter(TreeNode root) { 532 533 LinkedList<TreeNode> stack = new LinkedList<TreeNode>(); 534 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 535 536 TreeNode pNode = root; 537 for (;;) { 538 while (pNode != null) { // 訪問左子樹 539 stack.push(pNode); 540 pNode = pNode.getLchild(); 541 } 542 pNode = stack.pop(); 543 nodelist.add(pNode); // 訪問根結點 544 pNode = pNode.getRchild(); // 訪問右子樹 545 if (pNode == null && stack.isEmpty()) { break; } 546 } 547 548 return nodelist; 549 } 550 551 /** 552 * postOrderTraverse: 二叉樹的遞歸后序遍歷 553 */ 554 private List<TreeNode> postOrderTraverse(TreeNode root, List<TreeNode> nodelist) { 555 556 if (root != null) { 557 postOrderTraverse(root.getLchild(), nodelist); 558 postOrderTraverse(root.getRchild(), nodelist); 559 nodelist.add(root); 560 } 561 562 return nodelist; 563 } 564 565 /** 566 * postOrderTraverseIter: 二叉樹的非遞歸后序遍歷 567 */ 568 private List<TreeNode> postOrderTraverseIter(TreeNode root) { 569 570 LinkedList<TreeNode> stack = new LinkedList<TreeNode>(); 571 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 572 573 int flag = 0; // 標識是否訪問過右子樹; flag = 0 表示沒有訪問; flag = 1 表示已經訪問過 574 TreeNode pNode = root; 575 TreeNode tmpNode = null; 576 loop1: 577 for (;;) { 578 while (pNode != null) { // 訪問左子樹 579 stack.push(pNode); 580 pNode = pNode.getLchild(); 581 flag = 0; 582 } 583 loop2: 584 for (;;) { 585 if (!stack.isEmpty()) { 586 587 if (flag == 0) // 尚未訪問根結點的右子樹 588 { 589 pNode = stack.peek(); // 取根結點的右子樹,訪問其右子樹 590 pNode = pNode.getRchild(); 591 flag = 1; 592 continue loop1; 593 } 594 595 if (flag == 1) { // 已經訪問過右子樹 596 pNode = stack.pop(); 597 nodelist.add(pNode); // 訪問根結點,實際上是左右子樹均為空的葉子結點 598 tmpNode = pNode; // 訪問某個結點后,立即訪問其父結點的右子樹 599 pNode = stack.peek(); // 取該結點的父結點 600 if (pNode != null) { // 父結點不為空(沒有回溯到整棵樹的根結點) 601 if (tmpNode == pNode.getRchild()) { 602 // 如果剛剛訪問的結點正是其父結點的右孩子,則直接回溯訪問其父結點; 603 continue loop2; 604 } 605 else { // 否則,訪問其父結點的右子樹 606 pNode = pNode.getRchild(); 607 continue loop1; 608 } 609 } 610 611 } 612 613 614 } 615 else // 棧空,遞歸調用結束,退出 616 { 617 break loop1; 618 } 619 } 620 621 } 622 return nodelist; 623 } 624 625 /** 626 * floorOrderTraverse: 二叉樹的層序遍歷 627 * 628 * @param root 樹的根結點 629 * @return 樹的層序遍歷列表 630 * 631 */ 632 private List<TreeNode> floorOrderTraverse(TreeNode root) { 633 634 // 使用 LinkedList 來執行隊列的功能 635 LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); 636 List<TreeNode> nodelist = new ArrayList<TreeNode>(); 637 if (root != null) { 638 nodelist.add(root); 639 queue.addLast(root); 640 while(queue.size() > 0) { 641 TreeNode node = queue.removeFirst(); 642 if (node.getLchild() != null) { 643 nodelist.add(node.getLchild()); 644 queue.addLast(node.getLchild()); 645 } 646 if (node.getRchild() != null) { 647 nodelist.add(node.getRchild()); 648 queue.addLast(node.getRchild()); 649 } 650 } 651 } 652 653 return nodelist; 654 } 655 656 /** 657 * copyBinaryTree: 復制二叉樹 658 * @return 復制后的二叉樹 659 */ 660 public BinaryTree copyBinaryTree() 661 { 662 TreeNode anotherRoot = null; 663 anotherRoot = copy(getRoot()); 664 return new BinaryTree(anotherRoot); 665 } 666 667 /** 668 * copy: 復制二叉樹 669 * @param srcRoot 要復制的二叉樹的根結點 670 * @param destRoot 目標二叉樹的根結點 671 */ 672 private TreeNode copy(TreeNode srcRoot) 673 { 674 if (srcRoot != null) { 675 TreeNode newNode = new TreeNode(srcRoot.getCh(), null, null); 676 newNode.setLchild(copy(srcRoot.getLchild())); 677 newNode.setRchild(copy(srcRoot.getRchild())); 678 return newNode; 679 } 680 return null; 681 } 682 683 /** 684 * equalsTo: 比較該二叉樹與給定二叉樹 another 是否全等; 685 * 若全等則返回 true, 否則返回 false. 686 */ 687 public boolean equalsTo(BinaryTree another) 688 { 689 return compareEqual(root, another.getRoot()); 690 } 691 692 /** 693 * equalsTo : 比較給定的兩個二叉樹是否全等 694 * 兩個二叉樹全等當且僅當 695 * A. 兩個二叉樹均為空; 或者 696 * B. 兩個二叉樹均非空,且所有對應位置的結點都相同,對應結點之間的關聯也完全相同. 697 */ 698 private boolean compareEqual(TreeNode root, TreeNode anotherRoot) 699 { 700 return (root == null && anotherRoot == null) || 701 ((root != null && anotherRoot != null) && 702 (root.getCh() == anotherRoot.getCh()) && 703 (compareEqual(root.getLchild(), anotherRoot.getLchild())) && 704 (compareEqual(root.getRchild(), anotherRoot.getRchild()))); 705 } 706 707 /** 708 * swapTree : 將該二叉樹的所有結點的左右孩子互換 709 */ 710 public void swapTree() 711 { 712 StringBuilder sb = new StringBuilder(""); 713 swapTree(root); 714 createGeneralList(root, sb); 715 expression = sb.toString(); 716 } 717 718 /** 719 * swapTree : 將給定的二叉樹的所有結點的左右孩子互換 720 * @param root 給定二叉樹的根結點 721 */ 722 private void swapTree(TreeNode root) 723 { 724 if (root != null) { 725 TreeNode tmp = root.getLchild(); 726 root.setLchild(root.getRchild()); 727 root.setRchild(tmp); 728 swapTree(root.getLchild()); 729 swapTree(root.getRchild()); 730 } 731 } 732 733 /** 734 * longestPath: 獲取該二叉樹中的一條最長路徑 735 * @return 二叉樹中的一條最長路徑 736 */ 737 public List<TreeNode> longestPath() 738 { 739 List<TreeNode> longestPath = new ArrayList<TreeNode>(); 740 longestPath(root, longestPath); 741 return longestPath; 742 } 743 744 /** 745 * longestPath: 遞歸求解給定二叉樹的一條最長路徑 746 * @param root 給定二叉樹的根結點 747 * @param longestPath 存放二叉樹的最長路徑上的結點 748 */ 749 private void longestPath(TreeNode root, List<TreeNode> longestPath) 750 { 751 if (root != null) { 752 longestPath.add(root); 753 if (root.getLchild() == null && root.getRchild() == null) { // 左右子樹均空 754 return ; 755 } 756 if (root.getLchild() != null && root.getRchild() == null) { // 左子樹非空,右子樹為空,則最長路徑的結點必在左子樹路徑上 757 longestPath(root.getLchild(), longestPath); 758 } 759 if (root.getLchild() == null && root.getRchild() != null) { // 左子樹非空,右子樹為空,則最長路徑的結點必在右子樹路徑上 760 longestPath(root.getRchild(), longestPath); 761 } 762 if (root.getLchild() != null && root.getRchild() != null) { // 左右子樹均非空;分別求解左右子樹的最長路徑,取最大者 763 List<TreeNode> leftLongestPath = new ArrayList<TreeNode>(); 764 List<TreeNode> rightLongestPath = new ArrayList<TreeNode>(); 765 longestPath(root.getLchild(), leftLongestPath); 766 longestPath(root.getRchild(), rightLongestPath); 767 if (leftLongestPath.size() >= rightLongestPath.size()) { 768 longestPath.addAll(leftLongestPath); 769 } else if (leftLongestPath.size() < rightLongestPath.size()) { 770 longestPath.addAll(rightLongestPath); 771 } 772 773 } 774 } 775 } 776 777 778 779 /** 780 * listToString: 返回二叉樹的結點列表的字符串表示 781 * 782 * @param nodelist 樹的結點列表 783 * @return 二叉樹的結點列表的字符串表示 784 * 785 */ 786 public String listToString(List<TreeNode> nodelist) { 787 788 if (nodelist == null || nodelist.size() == 0) { 789 return "[ 空樹 ]"; 790 } 791 StringBuilder str = new StringBuilder("["); 792 Iterator<TreeNode> iter = nodelist.iterator(); 793 while (iter.hasNext()) { 794 TreeNode node = iter.next(); 795 str.append(node.getCh()+" "); 796 } 797 str.deleteCharAt(str.length()-1); 798 str.append(']'); 799 return str.toString(); 800 } 801 802 803 /** 804 * createBinaryTree: 根據前序和中序遍歷列表生成二叉樹 805 * 806 * @param preOrderList 前序列表字符串 807 * @param inOrderList 中序列表字符串 808 * @throws Exception 809 * 810 */ 811 public static BinaryTree createBinaryTree(String preOrderList, String inOrderList) throws Exception { 812 BinaryTree bt = new BinaryTree(getGeneralList(preOrderList, inOrderList)); 813 bt.createBinaryTree(); 814 return bt; 815 } 816 817 /** 818 * getGeneralist: 根據前序和中序遍歷列表生成二叉樹的廣義表表示【字符串形式】 819 * 820 * @param preOrderList 前序列表字符串 821 * @param inOrderList 中序列表字符串 822 * @return generalList 廣義表表示 823 * 824 */ 825 826 private static String getGeneralList(String preOrderList, String inOrderList) { 827 828 String s = ""; 829 if (preOrderList.length() > 0 || inOrderList.length() > 0) { 830 831 // 如果只含一個結點值,就直接返回 832 if (preOrderList.length() == 1) 833 return preOrderList; 834 835 // 根據前序遍歷, 第一個是根結點的值 836 char ch = preOrderList.charAt(0); 837 838 // 根據中序遍歷及根結點,將前序列表分為左右子樹列表。 839 int index = inOrderList.indexOf(ch); 840 String inOrderLeft = inOrderList.substring(0, index); // 左子樹的中序遍歷列表 841 String inOrderRight = inOrderList.substring(index+1); // 右子樹的中序遍歷列表 842 String preOrderLeft = preOrderList.substring(1, inOrderLeft.length()+1); // 左子樹的前序遍歷列表 843 String preOrderRight = preOrderList.substring(inOrderLeft.length()+1); // 右子樹的前序遍歷列表 844 s += ch; 845 s += "(" + getGeneralList(preOrderLeft,inOrderLeft) + "," + getGeneralList(preOrderRight,inOrderRight) + ")"; 846 } 847 return s; 848 } 849 850 /** 851 * createBinaryTree: 根據中序和后序遍歷列表生成二叉樹 852 * 853 * @param inOrderList 中序列表 854 * @param postOrderList 后序列表 855 * @param flag 標記 856 * 857 * @throws Exception 858 */ 859 public static BinaryTree createBinaryTree(String inOrderList, String postOrderList, boolean flag) throws Exception { 860 BinaryTree bt = new BinaryTree(getGeneralList(inOrderList, postOrderList, flag)); 861 bt.createBinaryTree(); 862 return bt; 863 } 864 865 866 867 /** 868 * getGeneralList: 根據中序和后序遍歷生成二叉樹的廣義表表示【字符串形式】 869 * 870 * @param inOrderList 中序列表 871 * @param postOrderList 后序列表 872 * @param flag 標記 873 * @return generalList 廣義表表示 874 * 875 */ 876 877 private static String getGeneralList(String inOrderList, String postOrderList, boolean flag) { 878 879 String s = ""; 880 if (inOrderList.length() > 0 || postOrderList.length() >0) { 881 882 // 如果只含一個結點值,就直接返回 883 if (inOrderList.length() == 1) 884 return inOrderList; 885 886 // 根據后序遍歷規則,最后一個是根結點的值 887 char ch = postOrderList.charAt(postOrderList.length()-1); 888 889 // 根據中序遍歷及根結點,將前序列表分為左右子樹列表。 890 int index = inOrderList.indexOf(ch); 891 String inOrderLeft = inOrderList.substring(0, index); // 左子樹的中序遍歷列表 892 String inOrderRight = inOrderList.substring(index+1); // 右子樹的中序遍歷列表 893 String postOrderLeft = postOrderList.substring(0, inOrderLeft.length()); // 左子樹的前序遍歷列表 894 String postOrderRight = postOrderList.substring(inOrderLeft.length(), 895 inOrderLeft.length()+inOrderRight.length()); // 右子樹的前序遍歷列表 896 s += ch; 897 s += "(" + getGeneralList(inOrderLeft,postOrderLeft,true) + "," + getGeneralList(inOrderRight,postOrderRight,true) + ")"; 898 } 899 return s; 900 } 901 902 /** 903 * trimSpace: 將廣義表表達式字符串中的空格符去掉 904 */ 905 private static String trimSpace(String s) 906 { 907 StringBuilder sb = new StringBuilder(""); 908 for (int i=0; i < s.length(); i++) { 909 char ch = s.charAt(i); 910 if (!(new Character(ch).toString().matches("//s"))) { 911 sb.append(ch); 912 } 913 } 914 return sb.toString(); 915 } 916 917 /** 918 * separatorIndex : 求解廣義表表達式中分割左右子樹的分隔符的位置 919 * 由於這里使用逗號分割左右子樹,則當且僅當其位置應當滿足: 920 * 在該分割符之前,左括號的數目恰好比右括號的數目多1. 921 * @return 若存在滿足條件的分隔符,則返回其位置;否則返回 -1 922 */ 923 private static int separatorIndex(String expression) 924 { 925 int leftBracketCounter=0, rightBacketCounter=0; 926 int index = 0; 927 for (; index < expression.length(); index++) { 928 char ch = expression.charAt(index); 929 if (ch == '(') { 930 leftBracketCounter++; 931 } 932 else if (ch == ')') { 933 rightBacketCounter++; 934 } 935 else if (ch == ',') { 936 if (leftBracketCounter == rightBacketCounter+1) break; 937 } 938 } 939 if (index < expression.length()) { 940 return index; 941 } 942 return -1; 943 } 944 945 }
