java 树面试题_java——二叉树面试题
1
2 importjava.util.ArrayList;3 importjava.util.Iterator;4 importjava.util.LinkedList;5 importjava.util.List;6 importjava.util.Queue;7 importjava.util.Stack;8
9 /**
10 *http://blog.csdn.net/luckyxiaoqiang/article/details/7518888輕松搞定面試中的二叉樹題目11 *http://www.cnblogs.com/Jax/archive/2009/12/28/1633691.html算法大全(3) 二叉樹12 *13 * TODO: 一定要能熟練地寫出所有問題的遞歸和非遞歸做法!14 *15 * 1. 求二叉樹中的節點個數: getNodeNumRec(遞歸),getNodeNum(迭代)16 * 2. 求二叉樹的深度: getDepthRec(遞歸),getDepth17 * 3. 前序遍歷,中序遍歷,后序遍歷: preorderTraversalRec, preorderTraversal, inorderTraversalRec, postorderTraversalRec18 * (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2)19 * 4.分層遍歷二叉樹(按層次從上往下,從左往右): levelTraversal, levelTraversalRec(遞歸解法!)20 * 5. 將二叉查找樹變為有序的雙向鏈表: convertBST2DLLRec, convertBST2DLL21 * 6. 求二叉樹第K層的節點個數:getNodeNumKthLevelRec, getNodeNumKthLevel22 * 7. 求二叉樹中葉子節點的個數:getNodeNumLeafRec, getNodeNumLeaf23 * 8. 判斷兩棵二叉樹是否相同的樹:isSameRec, isSame24 * 9. 判斷二叉樹是不是平衡二叉樹:isAVLRec25 * 10. 求二叉樹的鏡像(破壞和不破壞原來的樹兩種情況):mirrorRec, mirrorCopyRec26 * 10.1 判斷兩個樹是否互相鏡像:isMirrorRec27 * 11. 求二叉樹中兩個節點的最低公共祖先節點:getLastCommonParent, getLastCommonParentRec, getLastCommonParentRec228 * 12. 求二叉樹中節點的最大距離:getMaxDistanceRec29 * 13. 由前序遍歷序列和中序遍歷序列重建二叉樹:rebuildBinaryTreeRec30 * 14.判斷二叉樹是不是完全二叉樹:isCompleteBinaryTree, isCompleteBinaryTreeRec31 *32 */
33 public classDemo {34
35 /*
36 137 / \38 2 339 / \ \40 4 5 641 */
42 public static voidmain(String[] args) {43 TreeNode r1 = new TreeNode(1);44 TreeNode r2 = new TreeNode(2);45 TreeNode r3 = new TreeNode(3);46 TreeNode r4 = new TreeNode(4);47 TreeNode r5 = new TreeNode(5);48 TreeNode r6 = new TreeNode(6);49
50 r1.left =r2;51 r1.right =r3;52 r2.left =r4;53 r2.right =r5;54 r3.right =r6;55
56 //System.out.println(getNodeNumRec(r1));57 //System.out.println(getNodeNum(r1));58 //System.out.println(getDepthRec(r1));59 //System.out.println(getDepth(r1));60
61 //preorderTraversalRec(r1);62 //System.out.println();63 //preorderTraversal(r1);64 //System.out.println();65 //inorderTraversalRec(r1);66 //System.out.println();67 //inorderTraversal(r1);68 //System.out.println();69 //postorderTraversalRec(r1);70 //System.out.println();71 //postorderTraversal(r1);72 //System.out.println();73 //levelTraversal(r1);74 //System.out.println();75 //levelTraversalRec(r1);76 //System.out.println();77
78 //TreeNode tmp = convertBSTRec(r1);79 //while(true){80 //if(tmp == null){81 //break;82 //}83 //System.out.print(tmp.val + " ");84 //if(tmp.right == null){85 //break;86 //}87 //tmp = tmp.right;88 //}89 //System.out.println();90 //while(true){91 //if(tmp == null){92 //break;93 //}94 //System.out.print(tmp.val + " ");95 //if(tmp.left == null){96 //break;97 //}98 //tmp = tmp.left;99 //}100
101
102 //TreeNode tmp = convertBST2DLL(r1);103 //while(true){104 //if(tmp == null){105 //break;106 //}107 //System.out.print(tmp.val + " ");108 //if(tmp.right == null){109 //break;110 //}111 //tmp = tmp.right;112 //}113
114 //System.out.println(getNodeNumKthLevelRec(r1, 2));115 //System.out.println(getNodeNumKthLevel(r1, 2));116
117 //System.out.println(getNodeNumLeafRec(r1));118 //System.out.println(getNodeNumLeaf(r1));119
120 //System.out.println(isSame(r1, r1));121 //inorderTraversal(r1);122 //System.out.println();123 //mirror(r1);124 //TreeNode mirrorRoot = mirrorCopy(r1);125 //inorderTraversal(mirrorRoot);
126
127 System.out.println(isCompleteBinaryTree(r1));128 System.out.println(isCompleteBinaryTreeRec(r1));129
130 }131
132 private static classTreeNode {133 intval;134 TreeNode left;135 TreeNode right;136
137 public TreeNode(intval) {138 this.val =val;139 }140 }141
142 /**
143 * 求二叉樹中的節點個數遞歸解法: O(n)144 * (1)如果二叉樹為空,節點個數為0145 * (2)如果二叉樹不為空,二叉樹節點個數 = 左子樹節點個數 +146 * 右子樹節點個數 + 1147 */
148 public static intgetNodeNumRec(TreeNode root) {149 if (root == null) {150 return 0;151 } else{152 return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;153 }154 }155
156 /**
157 * 求二叉樹中的節點個數迭代解法O(n):基本思想同LevelOrderTraversal,158 * 即用一個Queue,在Java里面可以用LinkedList來模擬159 */
160 public static intgetNodeNum(TreeNode root) {161 if(root == null){162 return 0;163 }164 int count = 1;165 Queue queue = new LinkedList();166 queue.add(root);167
168 while(!queue.isEmpty()){169 TreeNode cur = queue.remove(); //從隊頭位置移除
170 if(cur.left != null){ //如果有左孩子,加到隊尾
171 queue.add(cur.left);172 count++;173 }174 if(cur.right != null){ //如果有右孩子,加到隊尾
175 queue.add(cur.right);176 count++;177 }178 }179
180 returncount;181 }182
183 /**
184 * 求二叉樹的深度(高度) 遞歸解法: O(n)185 * (1)如果二叉樹為空,二叉樹的深度為0186 * (2)如果二叉樹不為空,二叉樹的深度 = max(左子樹深度, 右子樹深度) + 1187 */
188 public static intgetDepthRec(TreeNode root) {189 if (root == null) {190 return 0;191 }192
193 int leftDepth =getDepthRec(root.left);194 int rightDepth =getDepthRec(root.right);195 return Math.max(leftDepth, rightDepth) + 1;196 }197
198 /**
199 * 求二叉樹的深度(高度) 迭代解法: O(n)200 * 基本思想同LevelOrderTraversal,還是用一個Queue201 */
202 public static intgetDepth(TreeNode root) {203 if(root == null){204 return 0;205 }206
207 int depth = 0; //深度
208 int currentLevelNodes = 1; //當前Level,node的數量
209 int nextLevelNodes = 0; //下一層Level,node的數量
210
211 LinkedList queue = new LinkedList();212 queue.add(root);213
214 while( !queue.isEmpty() ){215 TreeNode cur = queue.remove(); //從隊頭位置移除
216 currentLevelNodes--; //減少當前Level node的數量
217 if(cur.left != null){ //如果有左孩子,加到隊尾
218 queue.add(cur.left);219 nextLevelNodes++; //并增加下一層Level node的數量
220 }221 if(cur.right != null){ //如果有右孩子,加到隊尾
222 queue.add(cur.right);223 nextLevelNodes++;224 }225
226 if(currentLevelNodes == 0){ //說明已經遍歷完當前層的所有節點
227 depth++; //增加高度
228 currentLevelNodes = nextLevelNodes; //初始化下一層的遍歷
229 nextLevelNodes = 0;230 }231 }232
233 returndepth;234 }235
236
237
238 /**
239 * 前序遍歷,中序遍歷,后序遍歷 前序遍歷遞歸解法:240 * (1)如果二叉樹為空,空操作241 * (2)如果二叉樹不為空,訪問根節點,前序遍歷左子樹,前序遍歷右子樹242 */
243 public static voidpreorderTraversalRec(TreeNode root) {244 if (root == null) {245 return;246 }247 System.out.print(root.val + " ");248 preorderTraversalRec(root.left);249 preorderTraversalRec(root.right);250 }251
252 /**
253 * 前序遍歷迭代解法:用一個輔助stack,總是把右孩子放進棧254 *http://www.youtube.com/watch?v=uPTCbdHSFg4
255 */
256 public static voidpreorderTraversal(TreeNode root) {257 if(root == null){258 return;259 }260
261 Stack stack = new Stack(); //輔助stack
262 stack.push(root);263
264 while( !stack.isEmpty() ){265 TreeNode cur = stack.pop(); //出棧棧頂元素
266 System.out.print(cur.val + " ");267
268 //關鍵點:要先壓入右孩子,再壓入左孩子,這樣在出棧時會先打印左孩子再打印右孩子
269 if(cur.right != null){270 stack.push(cur.right);271 }272 if(cur.left != null){273 stack.push(cur.left);274 }275 }276 }277
278 /**
279 * 中序遍歷遞歸解法280 * (1)如果二叉樹為空,空操作。281 * (2)如果二叉樹不為空,中序遍歷左子樹,訪問根節點,中序遍歷右子樹282 */
283 public static voidinorderTraversalRec(TreeNode root) {284 if (root == null) {285 return;286 }287 inorderTraversalRec(root.left);288 System.out.print(root.val + " ");289 inorderTraversalRec(root.right);290 }291
292 /**
293 * 中序遍歷迭代解法 ,用棧先把根節點的所有左孩子都添加到棧內,294 * 然后輸出棧頂元素,再處理棧頂元素的右子樹295 *http://www.youtube.com/watch?v=50v1sJkjxoc
296 *297 * 還有一種方法能不用遞歸和棧,基于線索二叉樹的方法,較麻煩以后補上298 *http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/
299 */
300 public static voidinorderTraversal(TreeNode root){301 if(root == null){302 return;303 }304 Stack stack = new Stack();305 TreeNode cur =root;306
307 while( true){308 while(cur != null){ //先添加一個非空節點所有的左孩子到棧
309 stack.push(cur);310 cur =cur.left;311 }312
313 if(stack.isEmpty()){314 break;315 }316
317 //因為此時已經沒有左孩子了,所以輸出棧頂元素
318 cur =stack.pop();319 System.out.print(cur.val + " ");320 cur = cur.right; //準備處理右子樹
321 }322 }323
324 /**
325 * 后序遍歷遞歸解法326 * (1)如果二叉樹為空,空操作327 * (2)如果二叉樹不為空,后序遍歷左子樹,后序遍歷右子樹,訪問根節點328 */
329 public static voidpostorderTraversalRec(TreeNode root) {330 if (root == null) {331 return;332 }333 postorderTraversalRec(root.left);334 postorderTraversalRec(root.right);335 System.out.print(root.val + " ");336 }337
338 /**
339 * 后序遍歷迭代解法340 *http://www.youtube.com/watch?v=hv-mJUs5mvU
341 *342 */
343 public static voidpostorderTraversal(TreeNode root) {344 if (root == null) {345 return;346 }347
348 Stack s = new Stack(); //第一個stack用于添加node和它的左右孩子
349 Stack output = new Stack();//第二個stack用于翻轉第一個stack輸出
350
351 s.push(root);352 while( !s.isEmpty() ){ //確保所有元素都被翻轉轉移到第二個stack
353 TreeNode cur = s.pop(); //把棧頂元素添加到第二個stack
354 output.push(cur);355
356 if(cur.left != null){ //把棧頂元素的左孩子和右孩子分別添加入第一個stack
357 s.push(cur.left);358 }359 if(cur.right != null){360 s.push(cur.right);361 }362 }363
364 while( !output.isEmpty() ){ //遍歷輸出第二個stack,即為后序遍歷
365 System.out.print(output.pop().val + " ");366 }367 }368
369 /**
370 * 分層遍歷二叉樹(按層次從上往下,從左往右)迭代371 * 相當于廣度優先搜索,使用隊列實現。隊列初始化,將根節點壓入隊列。當隊列不為空,進行如下操作:彈出一個節點372 * ,訪問,若左子節點或右子節點不為空,將其壓入隊列373 */
374 public static voidlevelTraversal(TreeNode root) {375 if (root == null) {376 return;377 }378 LinkedList queue = new LinkedList();379 queue.push(root);380
381 while (!queue.isEmpty()) {382 TreeNode cur =queue.removeFirst();383 System.out.print(cur.val + " ");384 if (cur.left != null) {385 queue.add(cur.left);386 }387 if (cur.right != null) {388 queue.add(cur.right);389 }390 }391 }392
393 /**
394 * 分層遍歷二叉樹(遞歸)395 * 很少有人會用遞歸去做level traversal396 * 基本思想是用一個大的ArrayList,里面包含了每一層的ArrayList。397 * 大的ArrayList的size和level有關系398 *399 * 這是我目前見到的最好的遞歸解法!400 *http://discuss.leetcode.com/questions/49/binary-tree-level-order-traversal#answer-container-2543401 */
402 public static voidlevelTraversalRec(TreeNode root) {403 ArrayList> ret = new ArrayList>();404 dfs(root, 0, ret);405 System.out.println(ret);406 }407
408 private static void dfs(TreeNode root, int level, ArrayList>ret){409 if(root == null){410 return;411 }412
413 //添加一個新的ArrayList表示新的一層
414 if(level >=ret.size()){415 ret.add(new ArrayList());416 }417
418 ret.get(level).add(root.val); //把節點添加到表示那一層的ArrayList里
419 dfs(root.left, level+1, ret); //遞歸處理下一層的左子樹和右子樹
420 dfs(root.right, level+1, ret);421 }422
423
424 /**
425 * 將二叉查找樹變為有序的雙向鏈表 要求不能創建新節點,只調整指針。426 * 遞歸解法:427 * 參考了http://stackoverflow.com/questions/11511898/converting-a-binary-search-tree-to-doubly-linked-list#answer-11530016428 * 感覺是最清晰的遞歸解法,但要注意遞歸完,root會在鏈表的中間位置,因此要手動429 * 把root移到鏈表頭或鏈表尾430 */
431 public staticTreeNode convertBST2DLLRec(TreeNode root) {432 root =convertBST2DLLSubRec(root);433
434 //root會在鏈表的中間位置,因此要手動把root移到鏈表頭
435 while(root.left != null){436 root =root.left;437 }438 returnroot;439 }440
441 /**
442 * 遞歸轉換BST為雙向鏈表(DLL)443 */
444 public staticTreeNode convertBST2DLLSubRec(TreeNode root){445 if(root==null || (root.left==null && root.right==null)){446 returnroot;447 }448
449 TreeNode tmp = null;450 if(root.left != null){ //處理左子樹
451 tmp =convertBST2DLLSubRec(root.left);452 while(tmp.right != null){ //尋找最右節點
453 tmp =tmp.right;454 }455 tmp.right = root; //把左子樹處理后結果和root連接
456 root.left =tmp;457 }458 if(root.right != null){ //處理右子樹
459 tmp =convertBST2DLLSubRec(root.right);460 while(tmp.left != null){ //尋找最左節點
461 tmp =tmp.left;462 }463 tmp.left = root; //把右子樹處理后結果和root連接
464 root.right =tmp;465 }466 returnroot;467 }468
469 /**
470 * 將二叉查找樹變為有序的雙向鏈表 迭代解法471 // * 類似inorder traversal的做法472 */
473 public staticTreeNode convertBST2DLL(TreeNode root) {474 if(root == null){475 return null;476 }477 Stack stack = new Stack();478 TreeNode cur = root; //指向當前處理節點
479 TreeNode old = null; //指向前一個處理的節點
480 TreeNode head = null; //鏈表頭
481
482 while( true){483 while(cur != null){ //先添加一個非空節點所有的左孩子到棧
484 stack.push(cur);485 cur =cur.left;486 }487
488 if(stack.isEmpty()){489 break;490 }491
492 //因為此時已經沒有左孩子了,所以輸出棧頂元素
493 cur =stack.pop();494 if(old != null){495 old.right =cur;496 }497 if(head == null){ ///第一個節點為雙向鏈表頭節點
498 head =cur;499 }500
501 old = cur; //更新old
502 cur = cur.right; //準備處理右子樹
503 }504
505 returnhead;506 }507
508 /**
509 * 求二叉樹第K層的節點個數 遞歸解法:510 * (1)如果二叉樹為空或者k<1返回0511 * (2)如果二叉樹不為空并且k==1,返回1512 * (3)如果二叉樹不為空且k>1,返回root左子樹中k-1層的節點個數與root右子樹k-1層節點個數之和513 *514 * 求以root為根的k層節點數目 等價于 求以root左孩子為根的k-1層(因為少了root那一層)節點數目 加上515 * 以root右孩子為根的k-1層(因為少了root那一層)節點數目516 *517 * 所以遇到樹,先把它拆成左子樹和右子樹,把問題降解518 *519 */
520 public static int getNodeNumKthLevelRec(TreeNode root, intk) {521 if (root == null || k < 1) {522 return 0;523 }524
525 if (k == 1) {526 return 1;527 }528 int numLeft = getNodeNumKthLevelRec(root.left, k - 1); //求root左子樹的k-1層節點數
529 int numRight = getNodeNumKthLevelRec(root.right, k - 1); //求root右子樹的k-1層節點數
530 return numLeft +numRight;531 }532
533 /**
534 * 求二叉樹第K層的節點個數 迭代解法:535 * 同getDepth的迭代解法536 */
537 public static int getNodeNumKthLevel(TreeNode root, intk){538 if(root == null){539 return 0;540 }541 Queue queue = new LinkedList();542 queue.add(root);543
544 int i = 1;545 int currentLevelNodes = 1; //當前Level,node的數量
546 int nextLevelNodes = 0; //下一層Level,node的數量
547
548 while( !queue.isEmpty() && i
550 currentLevelNodes--; //減少當前Level node的數量
551 if(cur.left != null){ //如果有左孩子,加到隊尾
552 queue.add(cur.left);553 nextLevelNodes++; //并增加下一層Level node的數量
554 }555 if(cur.right != null){ //如果有右孩子,加到隊尾
556 queue.add(cur.right);557 nextLevelNodes++;558 }559
560 if(currentLevelNodes == 0){ //說明已經遍歷完當前層的所有節點
561 currentLevelNodes = nextLevelNodes; //初始化下一層的遍歷
562 nextLevelNodes = 0;563 i++; //進入到下一層
564 }565 }566
567 returncurrentLevelNodes;568 }569
570 /**
571 * 求二叉樹中葉子節點的個數(遞歸)572 */
573 public static intgetNodeNumLeafRec(TreeNode root) {574 //當root不存在,返回空
575 if (root == null) {576 return 0;577 }578
579 //當為葉子節點時返回1
580 if (root.left == null && root.right == null) {581 return 1;582 }583
584 //把一個樹拆成左子樹和右子樹之和,原理同上一題
585 return getNodeNumLeafRec(root.left) +getNodeNumLeafRec(root.right);586 }587
588 /**
589 * 求二叉樹中葉子節點的個數(迭代)590 * 還是基于Level order traversal591 */
592 public static intgetNodeNumLeaf(TreeNode root) {593 if(root == null){594 return 0;595 }596 Queue queue = new LinkedList();597 queue.add(root);598
599 int leafNodes = 0; //記錄上一個Level,node的數量
600
601 while( !queue.isEmpty() ){602 TreeNode cur = queue.remove(); //從隊頭位置移除
603 if(cur.left != null){ //如果有左孩子,加到隊尾
604 queue.add(cur.left);605 }606 if(cur.right != null){ //如果有右孩子,加到隊尾
607 queue.add(cur.right);608 }609 if(cur.left==null && cur.right==null){ //葉子節點
610 leafNodes++;611 }612 }613
614 returnleafNodes;615 }616
617 /**
618 * 判斷兩棵二叉樹是否相同的樹。619 * 遞歸解法:620 * (1)如果兩棵二叉樹都為空,返回真621 * (2)如果兩棵二叉樹一棵為空,另一棵不為空,返回假622 * (3)如果兩棵二叉樹都不為空,如果對應的左子樹和右子樹都同構返回真,其他返回假623 */
624 public static booleanisSameRec(TreeNode r1, TreeNode r2) {625 //如果兩棵二叉樹都為空,返回真
626 if (r1 == null && r2 == null) {627 return true;628 }629 //如果兩棵二叉樹一棵為空,另一棵不為空,返回假
630 else if (r1 == null || r2 == null) {631 return false;632 }633
634 if(r1.val !=r2.val){635 return false;636 }637 boolean leftRes = isSameRec(r1.left, r2.left); //比較對應左子樹
638 boolean rightRes = isSameRec(r1.right, r2.right); //比較對應右子樹
639 return leftRes &&rightRes;640 }641
642 /**
643 * 判斷兩棵二叉樹是否相同的樹(迭代)644 * 遍歷一遍即可,這里用preorder645 */
646 public static booleanisSame(TreeNode r1, TreeNode r2) {647 //如果兩個樹都是空樹,則返回true
648 if(r1==null && r2==null){649 return true;650 }651
652 //如果有一棵樹是空樹,另一顆不是,則返回false
653 if(r1==null || r2==null){654 return false;655 }656
657 Stack s1 = new Stack();658 Stack s2 = new Stack();659
660 s1.push(r1);661 s2.push(r2);662
663 while(!s1.isEmpty() && !s2.isEmpty()){664 TreeNode n1 =s1.pop();665 TreeNode n2 =s2.pop();666 if(n1==null && n2==null){667 continue;668 }else if(n1!=null && n2!=null && n1.val==n2.val){669 s1.push(n1.right);670 s1.push(n1.left);671 s2.push(n2.right);672 s2.push(n2.left);673 }else{674 return false;675 }676 }677 return true;678 }679
680 /**
681 * 判斷二叉樹是不是平衡二叉樹 遞歸解法:682 * (1)如果二叉樹為空,返回真683 * (2)如果二叉樹不為空,如果左子樹和右子樹都是AVL樹并且左子樹和右子樹高度相差不大于1,返回真,其他返回假684 */
685 public static booleanisAVLRec(TreeNode root) {686 if(root == null){ //如果二叉樹為空,返回真
687 return true;688 }689
690 //如果左子樹和右子樹高度相差大于1,則非平衡二叉樹, getDepthRec()是前面實現過的求樹高度的方法
691 if(Math.abs(getDepthRec(root.left) - getDepthRec(root.right)) > 1){692 return false;693 }694
695 //遞歸判斷左子樹和右子樹是否為平衡二叉樹
696 return isAVLRec(root.left) &&isAVLRec(root.right);697 }698
699
700 /**
701 * 求二叉樹的鏡像 遞歸解法:702 * (1)如果二叉樹為空,返回空703 * (2)如果二叉樹不為空,求左子樹和右子樹的鏡像,然后交換左子樹和右子樹704 */
705 //1. 破壞原來的樹,把原來的樹改成其鏡像
706 public staticTreeNode mirrorRec(TreeNode root) {707 if (root == null) {708 return null;709 }710
711 TreeNode left =mirrorRec(root.left);712 TreeNode right =mirrorRec(root.right);713
714 root.left =right;715 root.right =left;716 returnroot;717 }718
719 //2. 不能破壞原來的樹,返回一個新的鏡像樹
720 public staticTreeNode mirrorCopyRec(TreeNode root){721 if(root == null){722 return null;723 }724
725 TreeNode newNode = newTreeNode(root.val);726 newNode.left =mirrorCopyRec(root.right);727 newNode.right =mirrorCopyRec(root.left);728
729 returnnewNode;730 }731
732 //3. 判斷兩個樹是否互相鏡像
733 public static booleanisMirrorRec(TreeNode r1, TreeNode r2){734 //如果兩個樹都是空樹,則返回true
735 if(r1==null && r2==null){736 return true;737 }738
739 //如果有一棵樹是空樹,另一顆不是,則返回false
740 if(r1==null || r2==null){741 return false;742 }743
744 //如果兩個樹都非空樹,則先比較根節點
745 if(r1.val !=r2.val){746 return false;747 }748
749 //遞歸比較r1的左子樹的鏡像是不是r2右子樹 和750 //r1的右子樹的鏡像是不是r2左子樹
751 return isMirrorRec(r1.left, r2.right) &&isMirrorRec(r1.right, r2.left);752 }753
754 //1. 破壞原來的樹,把原來的樹改成其鏡像
755 public static voidmirror(TreeNode root) {756 if(root == null){757 return;758 }759
760 Stack stack = new Stack();761 stack.push(root);762 while( !stack.isEmpty() ){763 TreeNode cur =stack.pop();764
765 //交換左右孩子
766 TreeNode tmp =cur.right;767 cur.right =cur.left;768 cur.left =tmp;769
770 if(cur.right != null){771 stack.push(cur.right);772 }773 if(cur.left != null){774 stack.push(cur.left);775 }776 }777 }778
779 //2. 不能破壞原來的樹,返回一個新的鏡像樹
780 public staticTreeNode mirrorCopy(TreeNode root){781 if(root == null){782 return null;783 }784
785 Stack stack = new Stack();786 Stack newStack = new Stack();787 stack.push(root);788 TreeNode newRoot = newTreeNode(root.val);789 newStack.push(newRoot);790
791 while( !stack.isEmpty() ){792 TreeNode cur =stack.pop();793 TreeNode newCur =newStack.pop();794
795 if(cur.right != null){796 stack.push(cur.right);797 newCur.left = newTreeNode(cur.right.val);798 newStack.push(newCur.left);799 }800 if(cur.left != null){801 stack.push(cur.left);802 newCur.right = newTreeNode(cur.left.val);803 newStack.push(newCur.right);804 }805 }806
807 returnnewRoot;808 }809
810
811 /**
812 * 求二叉樹中兩個節點的最低公共祖先節點813 * 遞歸解法:814 * (1)如果兩個節點分別在根節點的左子樹和右子樹,則返回根節點815 * (2)如果兩個節點都在左子樹,則遞歸處理左子樹;如果兩個節點都在右子樹,則遞歸處理右子樹816 */
817 public staticTreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {818 if (findNodeRec(root.left, n1)) { //如果n1在樹的左子樹
819 if (findNodeRec(root.right, n2)) { //如果n2在樹的右子樹
820 return root; //返回根節點
821 } else { //如果n2也在樹的左子樹
822 return getLastCommonParentRec(root.left, n1, n2); //遞歸處理
823 }824 } else { //如果n1在樹的右子樹
825 if (findNodeRec(root.left, n2)) { //如果n2在左子樹
826 returnroot;827 } else { //如果n2在右子樹
828 return getLastCommonParentRec(root.right, n1, n2); //遞歸處理
829 }830 }831 }832
833 //幫助方法,遞歸判斷一個點是否在樹里
834 private static booleanfindNodeRec(TreeNode root, TreeNode node) {835 if (root == null || node == null) {836 return false;837 }838 if (root ==node) {839 return true;840 }841
842 //先嘗試在左子樹中查找
843 boolean found =findNodeRec(root.left, node);844 if (!found) { //如果查找不到,再在右子樹中查找
845 found =findNodeRec(root.right, node);846 }847 returnfound;848 }849
850 //求二叉樹中兩個節點的最低公共祖先節點 (更加簡潔版的遞歸)
851 public staticTreeNode getLastCommonParentRec2(TreeNode root, TreeNode n1, TreeNode n2) {852 if(root == null){853 return null;854 }855
856 //如果有一個match,則說明當前node就是要找的最低公共祖先
857 if(root.equals(n1) ||root.equals(n2)){858 returnroot;859 }860 TreeNode commonInLeft =getLastCommonParentRec2(root.left, n1, n2);861 TreeNode commonInRight =getLastCommonParentRec2(root.right, n1, n2);862
863 //如果一個左子樹找到,一個在右子樹找到,則說明root是唯一可能的最低公共祖先
864 if(commonInLeft!=null && commonInRight!=null){865 returnroot;866 }867
868 //其他情況是要不然在左子樹要不然在右子樹
869 if(commonInLeft != null){870 returncommonInLeft;871 }872
873 returncommonInRight;874 }875
876 /**
877 * 非遞歸解法:878 * 先求從根節點到兩個節點的路徑,然后再比較對應路徑的節點就行,最后一個相同的節點也就是他們在二叉樹中的最低公共祖先節點879 */
880 public staticTreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2) {881 if (root == null || n1 == null || n2 == null) {882 return null;883 }884
885 ArrayList p1 = new ArrayList();886 boolean res1 =getNodePath(root, n1, p1);887 ArrayList p2 = new ArrayList();888 boolean res2 =getNodePath(root, n2, p2);889
890 if (!res1 || !res2) {891 return null;892 }893
894 TreeNode last = null;895 Iterator iter1 =p1.iterator();896 Iterator iter2 =p2.iterator();897
898 while (iter1.hasNext() &&iter2.hasNext()) {899 TreeNode tmp1 =iter1.next();900 TreeNode tmp2 =iter2.next();901 if (tmp1 ==tmp2) {902 last =tmp1;903 } else { //直到遇到非公共節點
904 break;905 }906 }907 returnlast;908 }909
910 //把從根節點到node路徑上所有的點都添加到path中
911 private static boolean getNodePath(TreeNode root, TreeNode node, ArrayListpath) {912 if (root == null) {913 return false;914 }915
916 path.add(root); //把這個節點加到路徑中
917 if (root ==node) {918 return true;919 }920
921 boolean found = false;922 found = getNodePath(root.left, node, path); //先在左子樹中找
923
924 if (!found) { //如果沒找到,再在右子樹找
925 found =getNodePath(root.right, node, path);926 }927 if (!found) { //如果實在沒找到證明這個節點不在路徑中,說明剛才添加進去的不是路徑上的節點,刪掉!
928 path.remove(root);929 }930
931 returnfound;932 }933
934 /**
935 * 求二叉樹中節點的最大距離 即二叉樹中相距最遠的兩個節點之間的距離。 (distance / diameter)936 * 遞歸解法:937 * (1)如果二叉樹為空,返回0,同時記錄左子樹和右子樹的深度,都為0938 * (2)如果二叉樹不為空,最大距離要么是左子樹中的最大距離,要么是右子樹中的最大距離,939 * 要么是左子樹節點中到根節點的最大距離+右子樹節點中到根節點的最大距離,940 * 同時記錄左子樹和右子樹節點中到根節點的最大距離。941 *942 *http://www.cnblogs.com/miloyip/archive/2010/02/25/1673114.html
943 *944 * 計算一個二叉樹的最大距離有兩個情況:945
946 情況A: 路徑經過左子樹的最深節點,通過根節點,再到右子樹的最深節點。947 情況B: 路徑不穿過根節點,而是左子樹或右子樹的最大距離路徑,取其大者。948 只需要計算這兩個情況的路徑距離,并取其大者,就是該二叉樹的最大距離949 */
950 public staticResult getMaxDistanceRec(TreeNode root){951 if(root == null){952 Result empty = new Result(0, -1); //目的是讓調用方 +1 后,把當前的不存在的 (NULL) 子樹當成最大深度為 0
953 returnempty;954 }955
956 //計算出左右子樹分別最大距離
957 Result lmd =getMaxDistanceRec(root.left);958 Result rmd =getMaxDistanceRec(root.right);959
960 Result res = newResult();961 res.maxDepth = Math.max(lmd.maxDepth, rmd.maxDepth) + 1; //當前最大深度962 //取情況A和情況B中較大值
963 res.maxDistance = Math.max( lmd.maxDepth+rmd.maxDepth, Math.max(lmd.maxDistance, rmd.maxDistance) );964 returnres;965 }966
967 private static classResult{968 intmaxDistance;969 intmaxDepth;970 publicResult() {971 }972
973 public Result(int maxDistance, intmaxDepth) {974 this.maxDistance =maxDistance;975 this.maxDepth =maxDepth;976 }977 }978
979 /**
980 * 13. 由前序遍歷序列和中序遍歷序列重建二叉樹(遞歸)981 * 感覺這篇是講的最為清晰的:982 *http://crackinterviewtoday.wordpress.com/2010/03/15/rebuild-a-binary-tree-from-inorder-and-preorder-traversals/
983 * 文中還提到一種避免開額外空間的方法,等下次補上984 */
985 public static TreeNode rebuildBinaryTreeRec(List preOrder, ListinOrder){986 TreeNode root = null;987 ListleftPreOrder;988 ListrightPreOrder;989 ListleftInorder;990 ListrightInorder;991 intinorderPos;992 intpreorderPos;993
994 if ((preOrder.size() != 0) && (inOrder.size() != 0))995 {996 //把preorder的第一個元素作為root
997 root = new TreeNode(preOrder.get(0));998
999 //Based upon the current node data seperate the traversals into leftPreorder, rightPreorder,1000 //leftInorder, rightInorder lists1001 //因為知道root節點了,所以根據root節點位置,把preorder,inorder分別劃分為 root左側 和 右側 的兩個子區間
1002 inorderPos = inOrder.indexOf(preOrder.get(0)); //inorder序列的分割點
1003 leftInorder = inOrder.subList(0, inorderPos);1004 rightInorder = inOrder.subList(inorderPos + 1, inOrder.size());1005
1006 preorderPos = leftInorder.size(); //preorder序列的分割點
1007 leftPreOrder = preOrder.subList(1, preorderPos + 1);1008 rightPreOrder = preOrder.subList(preorderPos + 1, preOrder.size());1009
1010 root.left = rebuildBinaryTreeRec(leftPreOrder, leftInorder); //root的左子樹就是preorder和inorder的左側區間而形成的樹
1011 root.right = rebuildBinaryTreeRec(rightPreOrder, rightInorder); //root的右子樹就是preorder和inorder的右側區間而形成的樹
1012 }1013
1014 returnroot;1015 }1016
1017 /**
1018 14. 判斷二叉樹是不是完全二叉樹(迭代)1019 若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,1020 第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。1021 有如下算法,按層次(從上到下,從左到右)遍歷二叉樹,當遇到一個節點的左子樹為空時,1022 則該節點右子樹必須為空,且后面遍歷的節點左右子樹都必須為空,否則不是完全二叉樹。1023 */
1024 public static booleanisCompleteBinaryTree(TreeNode root){1025 if(root == null){1026 return false;1027 }1028
1029 Queue queue = new LinkedList();1030 queue.add(root);1031 boolean mustHaveNoChild = false;1032 boolean result = true;1033
1034 while( !queue.isEmpty() ){1035 TreeNode cur =queue.remove();1036 if(mustHaveNoChild){ //已經出現了有空子樹的節點了,后面出現的必須為葉節點(左右子樹都為空)
1037 if(cur.left!=null || cur.right!=null){1038 result = false;1039 break;1040 }1041 } else{1042 if(cur.left!=null && cur.right!=null){ //如果左子樹和右子樹都非空,則繼續遍歷
1043 queue.add(cur.left);1044 queue.add(cur.right);1045 }else if(cur.left!=null && cur.right==null){ //如果左子樹非空但右子樹為空,說明已經出現空節點,之后必須都為空子樹
1046 mustHaveNoChild = true;1047 queue.add(cur.left);1048 }else if(cur.left==null && cur.right!=null){ //如果左子樹為空但右子樹非空,說明這棵樹已經不是完全二叉完全樹!
1049 result = false;1050 break;1051 }else{ //如果左右子樹都為空,則后面的必須也都為空子樹
1052 mustHaveNoChild = true;1053 }1054 }1055 }1056 returnresult;1057 }1058
1059 /**
1060 * 14. 判斷二叉樹是不是完全二叉樹(遞歸)1061 *http://stackoverflow.com/questions/1442674/how-to-determine-whether-a-binary-tree-is-complete
1062 *1063 */
1064 public static booleanisCompleteBinaryTreeRec(TreeNode root){1065 //Pair notComplete = new Pair(-1, false);1066 //return !isCompleteBinaryTreeSubRec(root).equalsTo(notComplete);
1067 return isCompleteBinaryTreeSubRec(root).height != -1;1068 }1069
1070 //遞歸判斷是否滿樹(完美)
1071 public static booleanisPerfectBinaryTreeRec(TreeNode root){1072 returnisCompleteBinaryTreeSubRec(root).isFull;1073 }1074
1075 //遞歸,要創建一個Pair class來保存樹的高度和是否已滿的信息
1076 public staticPair isCompleteBinaryTreeSubRec(TreeNode root){1077 if(root == null){1078 return new Pair(0, true);1079 }1080
1081 Pair left =isCompleteBinaryTreeSubRec(root.left);1082 Pair right =isCompleteBinaryTreeSubRec(root.right);1083
1084 //左樹滿節點,而且左右樹相同高度,則是唯一可能形成滿樹(若右樹也是滿節點)的情況
1085 if(left.isFull && left.height==right.height){1086 return new Pair(1+left.height, right.isFull);1087 }1088
1089 //左樹非滿,但右樹是滿節點,且左樹高度比右樹高一1090 //注意到如果其左樹為非完全樹,則它的高度已經被設置成-1,1091 //因此不可能滿足第二個條件!
1092 if(right.isFull && left.height==right.height+1){1093 return new Pair(1+left.height, false);1094 }1095
1096 //其他情況都是非完全樹,直接設置高度為-1
1097 return new Pair(-1, false);1098 }1099
1100 private static classPair{1101 int height; //樹的高度
1102 boolean isFull; //是否是個滿樹
1103
1104 public Pair(int height, booleanisFull) {1105 this.height =height;1106 this.isFull =isFull;1107 }1108
1109 public booleanequalsTo(Pair obj){1110 return this.height==obj.height && this.isFull==obj.isFull;1111 }1112 }1113
1114 }
總結
以上是生活随笔為你收集整理的java 树面试题_java——二叉树面试题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bzoj3238 [Ahoi2013]差
- 下一篇: java 独占锁_锁分类(独占锁、分拆锁