https://blog.csdn.net/fightforyourdream/article/details/16843303

面试大总结之二:Java搞定面试中的二叉树题目

2013年11月20日 14:04:27 chiiis 阅读数:25438更多

个人分类: AlgorithmInterview

这是本系列的第二篇,与前一篇 面试大总结之一:Java搞定面试中的链表题目 相比,二叉树的题目可以变化的就更多了。本文还是参考整合重写了《轻松搞定面试中的二叉树题目》和《算法大全(3) 二叉树》两篇大作。本文一个小亮点就是几乎每一道题都用了递归和迭代两种方法写过一遍,因为面试时往往可能会被要求写不擅长的那一种。这一千多行的记录也是我在面试摸索过程中的一个小笔记,备份与此。请大神们轻拍指正。今后我也会不断更新加入新题新方法。

  1. package BinaryTreeSummary;

  2. import java.util.ArrayList;

  3. import java.util.Iterator;

  4. import java.util.LinkedList;

  5. import java.util.List;

  6. import java.util.Queue;

  7. import java.util.Stack;

  8. /**

  9. * http://blog.csdn.net/luckyxiaoqiang/article/details/7518888 轻松搞定面试中的二叉树题目

  10. * http://www.cnblogs.com/Jax/archive/2009/12/28/1633691.html 算法大全(3) 二叉树

  11. *

  12. * TODO: 一定要能熟练地写出所有问题的递归和非递归做法!

  13. *

  14. * 1. 求二叉树中的节点个数: getNodeNumRec(递归),getNodeNum(迭代)

  15. * 2. 求二叉树的深度: getDepthRec(递归),getDepth

  16. * 3. 前序遍历,中序遍历,后序遍历: preorderTraversalRec, preorderTraversal, inorderTraversalRec, postorderTraversalRec

  17. * (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2)

  18. * 4.分层遍历二叉树(按层次从上往下,从左往右): levelTraversal, levelTraversalRec(递归解法!)

  19. * 5. 将二叉查找树变为有序的双向链表: convertBST2DLLRec, convertBST2DLL

  20. * 6. 求二叉树第K层的节点个数:getNodeNumKthLevelRec, getNodeNumKthLevel

  21. * 7. 求二叉树中叶子节点的个数:getNodeNumLeafRec, getNodeNumLeaf

  22. * 8. 判断两棵二叉树是否相同的树:isSameRec, isSame

  23. * 9. 判断二叉树是不是平衡二叉树:isAVLRec

  24. * 10. 求二叉树的镜像(破坏和不破坏原来的树两种情况):mirrorRec, mirrorCopyRec

  25. * 10.1 判断两个树是否互相镜像:isMirrorRec

  26. * 11. 求二叉树中两个节点的最低公共祖先节点:getLastCommonParent, getLastCommonParentRec, getLastCommonParentRec2

  27. * 12. 求二叉树中节点的最大距离:getMaxDistanceRec

  28. * 13. 由前序遍历序列和中序遍历序列重建二叉树:rebuildBinaryTreeRec

  29. * 14.判断二叉树是不是完全二叉树:isCompleteBinaryTree, isCompleteBinaryTreeRec

  30. *

  31. */

  32. public class Demo {

  33. /*

  34. 1

  35. / \

  36. 2 3

  37. / \ \

  38. 4 5 6

  39. */

  40. public static void main(String[] args) {

  41. TreeNode r1 = new TreeNode(1);

  42. TreeNode r2 = new TreeNode(2);

  43. TreeNode r3 = new TreeNode(3);

  44. TreeNode r4 = new TreeNode(4);

  45. TreeNode r5 = new TreeNode(5);

  46. TreeNode r6 = new TreeNode(6);

  47. r1.left = r2;

  48. r1.right = r3;

  49. r2.left = r4;

  50. r2.right = r5;

  51. r3.right = r6;

  52. // System.out.println(getNodeNumRec(r1));

  53. // System.out.println(getNodeNum(r1));

  54. // System.out.println(getDepthRec(r1));

  55. // System.out.println(getDepth(r1));

  56. // preorderTraversalRec(r1);

  57. // System.out.println();

  58. // preorderTraversal(r1);

  59. // System.out.println();

  60. // inorderTraversalRec(r1);

  61. // System.out.println();

  62. // inorderTraversal(r1);

  63. // System.out.println();

  64. // postorderTraversalRec(r1);

  65. // System.out.println();

  66. // postorderTraversal(r1);

  67. // System.out.println();

  68. // levelTraversal(r1);

  69. // System.out.println();

  70. // levelTraversalRec(r1);

  71. // System.out.println();

  72. // TreeNode tmp = convertBSTRec(r1);

  73. // while(true){

  74. // if(tmp == null){

  75. // break;

  76. // }

  77. // System.out.print(tmp.val + " ");

  78. // if(tmp.right == null){

  79. // break;

  80. // }

  81. // tmp = tmp.right;

  82. // }

  83. // System.out.println();

  84. // while(true){

  85. // if(tmp == null){

  86. // break;

  87. // }

  88. // System.out.print(tmp.val + " ");

  89. // if(tmp.left == null){

  90. // break;

  91. // }

  92. // tmp = tmp.left;

  93. // }

  94. // TreeNode tmp = convertBST2DLL(r1);

  95. // while(true){

  96. // if(tmp == null){

  97. // break;

  98. // }

  99. // System.out.print(tmp.val + " ");

  100. // if(tmp.right == null){

  101. // break;

  102. // }

  103. // tmp = tmp.right;

  104. // }

  105. // System.out.println(getNodeNumKthLevelRec(r1, 2));

  106. // System.out.println(getNodeNumKthLevel(r1, 2));

  107. // System.out.println(getNodeNumLeafRec(r1));

  108. // System.out.println(getNodeNumLeaf(r1));

  109. // System.out.println(isSame(r1, r1));

  110. // inorderTraversal(r1);

  111. // System.out.println();

  112. // mirror(r1);

  113. // TreeNode mirrorRoot = mirrorCopy(r1);

  114. // inorderTraversal(mirrorRoot);

  115. System.out.println(isCompleteBinaryTree(r1));

  116. System.out.println(isCompleteBinaryTreeRec(r1));

  117. }

  118. private static class TreeNode {

  119. int val;

  120. TreeNode left;

  121. TreeNode right;

  122. public TreeNode(int val) {

  123. this.val = val;

  124. }

  125. }

  126. /**

  127. * 求二叉树中的节点个数递归解法: O(n)

  128. * (1)如果二叉树为空,节点个数为0

  129. * (2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 +

  130. * 右子树节点个数 + 1

  131. */

  132. public static int getNodeNumRec(TreeNode root) {

  133. if (root == null) {

  134. return 0;

  135. } else {

  136. return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;

  137. }

  138. }

  139. /**

  140. * 求二叉树中的节点个数迭代解法O(n):基本思想同LevelOrderTraversal,

  141. * 即用一个Queue,在Java里面可以用LinkedList来模拟

  142. */

  143. public static int getNodeNum(TreeNode root) {

  144. if(root == null){

  145. return 0;

  146. }

  147. int count = 1;

  148. Queue<TreeNode> queue = new LinkedList<TreeNode>();

  149. queue.add(root);

  150. while(!queue.isEmpty()){

  151. TreeNode cur = queue.remove(); // 从队头位置移除

  152. if(cur.left != null){ // 如果有左孩子,加到队尾

  153. queue.add(cur.left);

  154. count++;

  155. }

  156. if(cur.right != null){ // 如果有右孩子,加到队尾

  157. queue.add(cur.right);

  158. count++;

  159. }

  160. }

  161. return count;

  162. }

  163. /**

  164. * 求二叉树的深度(高度) 递归解法: O(n)

  165. * (1)如果二叉树为空,二叉树的深度为0

  166. * (2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1

  167. */

  168. public static int getDepthRec(TreeNode root) {

  169. if (root == null) {

  170. return 0;

  171. }

  172. int leftDepth = getDepthRec(root.left);

  173. int rightDepth = getDepthRec(root.right);

  174. return Math.max(leftDepth, rightDepth) + 1;

  175. }

  176. /**

  177. * 求二叉树的深度(高度) 迭代解法: O(n)

  178. * 基本思想同LevelOrderTraversal,还是用一个Queue

  179. */

  180. public static int getDepth(TreeNode root) {

  181. if(root == null){

  182. return 0;

  183. }

  184. int depth = 0; // 深度

  185. int currentLevelNodes = 1; // 当前Level,node的数量

  186. int nextLevelNodes = 0; // 下一层Level,node的数量

  187. LinkedList<TreeNode> queue = new LinkedList<TreeNode>();

  188. queue.add(root);

  189. while( !queue.isEmpty() ){

  190. TreeNode cur = queue.remove(); // 从队头位置移除

  191. currentLevelNodes--; // 减少当前Level node的数量

  192. if(cur.left != null){ // 如果有左孩子,加到队尾

  193. queue.add(cur.left);

  194. nextLevelNodes++; // 并增加下一层Level node的数量

  195. }

  196. if(cur.right != null){ // 如果有右孩子,加到队尾

  197. queue.add(cur.right);

  198. nextLevelNodes++;

  199. }

  200. if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点

  201. depth++; // 增加高度

  202. currentLevelNodes = nextLevelNodes; // 初始化下一层的遍历

  203. nextLevelNodes = 0;

  204. }

  205. }

  206. return depth;

  207. }

  208. /**

  209. * 前序遍历,中序遍历,后序遍历 前序遍历递归解法:

  210. * (1)如果二叉树为空,空操作

  211. * (2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树

  212. */

  213. public static void preorderTraversalRec(TreeNode root) {

  214. if (root == null) {

  215. return;

  216. }

  217. System.out.print(root.val + " ");

  218. preorderTraversalRec(root.left);

  219. preorderTraversalRec(root.right);

  220. }

  221. /**

  222. * 前序遍历迭代解法:用一个辅助stack,总是把右孩子放进栈

  223. * http://www.youtube.com/watch?v=uPTCbdHSFg4

  224. */

  225. public static void preorderTraversal(TreeNode root) {

  226. if(root == null){

  227. return;

  228. }

  229. Stack<TreeNode> stack = new Stack<TreeNode>(); // 辅助stack

  230. stack.push(root);

  231. while( !stack.isEmpty() ){

  232. TreeNode cur = stack.pop(); // 出栈栈顶元素

  233. System.out.print(cur.val + " ");

  234. // 关键点:要先压入右孩子,再压入左孩子,这样在出栈时会先打印左孩子再打印右孩子

  235. if(cur.right != null){

  236. stack.push(cur.right);

  237. }

  238. if(cur.left != null){

  239. stack.push(cur.left);

  240. }

  241. }

  242. }

  243. /**

  244. * 中序遍历递归解法

  245. * (1)如果二叉树为空,空操作。

  246. * (2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树

  247. */

  248. public static void inorderTraversalRec(TreeNode root) {

  249. if (root == null) {

  250. return;

  251. }

  252. inorderTraversalRec(root.left);

  253. System.out.print(root.val + " ");

  254. inorderTraversalRec(root.right);

  255. }

  256. /**

  257. * 中序遍历迭代解法 ,用栈先把根节点的所有左孩子都添加到栈内,

  258. * 然后输出栈顶元素,再处理栈顶元素的右子树

  259. * http://www.youtube.com/watch?v=50v1sJkjxoc

  260. *

  261. * 还有一种方法能不用递归和栈,基于线索二叉树的方法,较麻烦以后补上

  262. * http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/

  263. */

  264. public static void inorderTraversal(TreeNode root){

  265. if(root == null){

  266. return;

  267. }

  268. Stack<TreeNode> stack = new Stack<TreeNode>();

  269. TreeNode cur = root;

  270. while( true ){

  271. while(cur != null){ // 先添加一个非空节点所有的左孩子到栈

  272. stack.push(cur);

  273. cur = cur.left;

  274. }

  275. if(stack.isEmpty()){

  276. break;

  277. }

  278. // 因为此时已经没有左孩子了,所以输出栈顶元素

  279. cur = stack.pop();

  280. System.out.print(cur.val + " ");

  281. cur = cur.right; // 准备处理右子树

  282. }

  283. }

  284. /**

  285. * 后序遍历递归解法

  286. * (1)如果二叉树为空,空操作

  287. * (2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点

  288. */

  289. public static void postorderTraversalRec(TreeNode root) {

  290. if (root == null) {

  291. return;

  292. }

  293. postorderTraversalRec(root.left);

  294. postorderTraversalRec(root.right);

  295. System.out.print(root.val + " ");

  296. }

  297. /**

  298. * 后序遍历迭代解法

  299. * http://www.youtube.com/watch?v=hv-mJUs5mvU

  300. *

  301. */

  302. public static void postorderTraversal(TreeNode root) {

  303. if (root == null) {

  304. return;

  305. }

  306. Stack<TreeNode> s = new Stack<TreeNode>(); // 第一个stack用于添加node和它的左右孩子

  307. Stack<TreeNode> output = new Stack<TreeNode>();// 第二个stack用于翻转第一个stack输出

  308. s.push(root);

  309. while( !s.isEmpty() ){ // 确保所有元素都被翻转转移到第二个stack

  310. TreeNode cur = s.pop(); // 把栈顶元素添加到第二个stack

  311. output.push(cur);

  312. if(cur.left != null){ // 把栈顶元素的左孩子和右孩子分别添加入第一个stack

  313. s.push(cur.left);

  314. }

  315. if(cur.right != null){

  316. s.push(cur.right);

  317. }

  318. }

  319. while( !output.isEmpty() ){ // 遍历输出第二个stack,即为后序遍历

  320. System.out.print(output.pop().val + " ");

  321. }

  322. }

  323. /**

  324. * 分层遍历二叉树(按层次从上往下,从左往右)迭代

  325. * 相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点

  326. * ,访问,若左子节点或右子节点不为空,将其压入队列

  327. */

  328. public static void levelTraversal(TreeNode root) {

  329. if (root == null) {

  330. return;

  331. }

  332. LinkedList<TreeNode> queue = new LinkedList<TreeNode>();

  333. queue.push(root);

  334. while (!queue.isEmpty()) {

  335. TreeNode cur = queue.removeFirst();

  336. System.out.print(cur.val + " ");

  337. if (cur.left != null) {

  338. queue.add(cur.left);

  339. }

  340. if (cur.right != null) {

  341. queue.add(cur.right);

  342. }

  343. }

  344. }

  345. /**

  346. * 分层遍历二叉树(递归)

  347. * 很少有人会用递归去做level traversal

  348. * 基本思想是用一个大的ArrayList,里面包含了每一层的ArrayList。

  349. * 大的ArrayList的size和level有关系

  350. *

  351. * 这是我目前见到的最好的递归解法!

  352. * http://discuss.leetcode.com/questions/49/binary-tree-level-order-traversal#answer-container-2543

  353. */

  354. public static void levelTraversalRec(TreeNode root) {

  355. ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();

  356. dfs(root, 0, ret);

  357. System.out.println(ret);

  358. }

  359. private static void dfs(TreeNode root, int level, ArrayList<ArrayList<Integer>> ret){

  360. if(root == null){

  361. return;

  362. }

  363. // 添加一个新的ArrayList表示新的一层

  364. if(level >= ret.size()){

  365. ret.add(new ArrayList<Integer>());

  366. }

  367. ret.get(level).add(root.val); // 把节点添加到表示那一层的ArrayList里

  368. dfs(root.left, level+1, ret); // 递归处理下一层的左子树和右子树

  369. dfs(root.right, level+1, ret);

  370. }

  371. /**

  372. * 将二叉查找树变为有序的双向链表 要求不能创建新节点,只调整指针。

  373. * 递归解法:

  374. * 参考了http://stackoverflow.com/questions/11511898/converting-a-binary-search-tree-to-doubly-linked-list#answer-11530016

  375. * 感觉是最清晰的递归解法,但要注意递归完,root会在链表的中间位置,因此要手动

  376. * 把root移到链表头或链表尾

  377. */

  378. public static TreeNode convertBST2DLLRec(TreeNode root) {

  379. root = convertBST2DLLSubRec(root);

  380. // root会在链表的中间位置,因此要手动把root移到链表头

  381. while(root.left != null){

  382. root = root.left;

  383. }

  384. return root;

  385. }

  386. /**

  387. * 递归转换BST为双向链表(DLL)

  388. */

  389. public static TreeNode convertBST2DLLSubRec(TreeNode root){

  390. if(root==null || (root.left==null && root.right==null)){

  391. return root;

  392. }

  393. TreeNode tmp = null;

  394. if(root.left != null){ // 处理左子树

  395. tmp = convertBST2DLLSubRec(root.left);

  396. while(tmp.right != null){ // 寻找最右节点

  397. tmp = tmp.right;

  398. }

  399. tmp.right = root; // 把左子树处理后结果和root连接

  400. root.left = tmp;

  401. }

  402. if(root.right != null){ // 处理右子树

  403. tmp = convertBST2DLLSubRec(root.right);

  404. while(tmp.left != null){ // 寻找最左节点

  405. tmp = tmp.left;

  406. }

  407. tmp.left = root; // 把右子树处理后结果和root连接

  408. root.right = tmp;

  409. }

  410. return root;

  411. }

  412. /**

  413. * 将二叉查找树变为有序的双向链表 迭代解法

  414. // * 类似inorder traversal的做法

  415. */

  416. public static TreeNode convertBST2DLL(TreeNode root) {

  417. if(root == null){

  418. return null;

  419. }

  420. Stack<TreeNode> stack = new Stack<TreeNode>();

  421. TreeNode cur = root; // 指向当前处理节点

  422. TreeNode old = null; // 指向前一个处理的节点

  423. TreeNode head = null; // 链表头

  424. while( true ){

  425. while(cur != null){ // 先添加一个非空节点所有的左孩子到栈

  426. stack.push(cur);

  427. cur = cur.left;

  428. }

  429. if(stack.isEmpty()){

  430. break;

  431. }

  432. // 因为此时已经没有左孩子了,所以输出栈顶元素

  433. cur = stack.pop();

  434. if(old != null){

  435. old.right = cur;

  436. }

  437. if(head == null){ // /第一个节点为双向链表头节点

  438. head = cur;

  439. }

  440. old = cur; // 更新old

  441. cur = cur.right; // 准备处理右子树

  442. }

  443. return head;

  444. }

  445. /**

  446. * 求二叉树第K层的节点个数 递归解法:

  447. * (1)如果二叉树为空或者k<1返回0

  448. * (2)如果二叉树不为空并且k==1,返回1

  449. * (3)如果二叉树不为空且k>1,返回root左子树中k-1层的节点个数与root右子树k-1层节点个数之和

  450. *

  451. * 求以root为根的k层节点数目 等价于 求以root左孩子为根的k-1层(因为少了root那一层)节点数目 加上

  452. * 以root右孩子为根的k-1层(因为少了root那一层)节点数目

  453. *

  454. * 所以遇到树,先把它拆成左子树和右子树,把问题降解

  455. *

  456. */

  457. public static int getNodeNumKthLevelRec(TreeNode root, int k) {

  458. if (root == null || k < 1) {

  459. return 0;

  460. }

  461. if (k == 1) {

  462. return 1;

  463. }

  464. int numLeft = getNodeNumKthLevelRec(root.left, k - 1); // 求root左子树的k-1层节点数

  465. int numRight = getNodeNumKthLevelRec(root.right, k - 1); // 求root右子树的k-1层节点数

  466. return numLeft + numRight;

  467. }

  468. /**

  469. * 求二叉树第K层的节点个数 迭代解法:

  470. * 同getDepth的迭代解法

  471. */

  472. public static int getNodeNumKthLevel(TreeNode root, int k){

  473. if(root == null){

  474. return 0;

  475. }

  476. Queue<TreeNode> queue = new LinkedList<TreeNode>();

  477. queue.add(root);

  478. int i = 1;

  479. int currentLevelNodes = 1; // 当前Level,node的数量

  480. int nextLevelNodes = 0; // 下一层Level,node的数量

  481. while( !queue.isEmpty() && i<k){

  482. TreeNode cur = queue.remove(); // 从队头位置移除

  483. currentLevelNodes--; // 减少当前Level node的数量

  484. if(cur.left != null){ // 如果有左孩子,加到队尾

  485. queue.add(cur.left);

  486. nextLevelNodes++; // 并增加下一层Level node的数量

  487. }

  488. if(cur.right != null){ // 如果有右孩子,加到队尾

  489. queue.add(cur.right);

  490. nextLevelNodes++;

  491. }

  492. if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点

  493. currentLevelNodes = nextLevelNodes; // 初始化下一层的遍历

  494. nextLevelNodes = 0;

  495. i++; // 进入到下一层

  496. }

  497. }

  498. return currentLevelNodes;

  499. }

  500. /**

  501. * 求二叉树中叶子节点的个数(递归)

  502. */

  503. public static int getNodeNumLeafRec(TreeNode root) {

  504. // 当root不存在,返回空

  505. if (root == null) {

  506. return 0;

  507. }

  508. // 当为叶子节点时返回1

  509. if (root.left == null && root.right == null) {

  510. return 1;

  511. }

  512. // 把一个树拆成左子树和右子树之和,原理同上一题

  513. return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);

  514. }

  515. /**

  516. * 求二叉树中叶子节点的个数(迭代)

  517. * 还是基于Level order traversal

  518. */

  519. public static int getNodeNumLeaf(TreeNode root) {

  520. if(root == null){

  521. return 0;

  522. }

  523. Queue<TreeNode> queue = new LinkedList<TreeNode>();

  524. queue.add(root);

  525. int leafNodes = 0; // 记录上一个Level,node的数量

  526. while( !queue.isEmpty() ){

  527. TreeNode cur = queue.remove(); // 从队头位置移除

  528. if(cur.left != null){ // 如果有左孩子,加到队尾

  529. queue.add(cur.left);

  530. }

  531. if(cur.right != null){ // 如果有右孩子,加到队尾

  532. queue.add(cur.right);

  533. }

  534. if(cur.left==null && cur.right==null){ // 叶子节点

  535. leafNodes++;

  536. }

  537. }

  538. return leafNodes;

  539. }

  540. /**

  541. * 判断两棵二叉树是否相同的树。

  542. * 递归解法:

  543. * (1)如果两棵二叉树都为空,返回真

  544. * (2)如果两棵二叉树一棵为空,另一棵不为空,返回假

  545. * (3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假

  546. */

  547. public static boolean isSameRec(TreeNode r1, TreeNode r2) {

  548. // 如果两棵二叉树都为空,返回真

  549. if (r1 == null && r2 == null) {

  550. return true;

  551. }

  552. // 如果两棵二叉树一棵为空,另一棵不为空,返回假

  553. else if (r1 == null || r2 == null) {

  554. return false;

  555. }

  556. if(r1.val != r2.val){

  557. return false;

  558. }

  559. boolean leftRes = isSameRec(r1.left, r2.left); // 比较对应左子树

  560. boolean rightRes = isSameRec(r1.right, r2.right); // 比较对应右子树

  561. return leftRes && rightRes;

  562. }

  563. /**

  564. * 判断两棵二叉树是否相同的树(迭代)

  565. * 遍历一遍即可,这里用preorder

  566. */

  567. public static boolean isSame(TreeNode r1, TreeNode r2) {

  568. // 如果两个树都是空树,则返回true

  569. if(r1==null && r2==null){

  570. return true;

  571. }

  572. // 如果有一棵树是空树,另一颗不是,则返回false

  573. if(r1==null || r2==null){

  574. return false;

  575. }

  576. Stack<TreeNode> s1 = new Stack<TreeNode>();

  577. Stack<TreeNode> s2 = new Stack<TreeNode>();

  578. s1.push(r1);

  579. s2.push(r2);

  580. while(!s1.isEmpty() && !s2.isEmpty()){

  581. TreeNode n1 = s1.pop();

  582. TreeNode n2 = s2.pop();

  583. if(n1==null && n2==null){

  584. continue;

  585. }else if(n1!=null && n2!=null && n1.val==n2.val){

  586. s1.push(n1.right);

  587. s1.push(n1.left);

  588. s2.push(n2.right);

  589. s2.push(n2.left);

  590. }else{

  591. return false;

  592. }

  593. }

  594. return true;

  595. }

  596. /**

  597. * 判断二叉树是不是平衡二叉树 递归解法:

  598. * (1)如果二叉树为空,返回真

  599. * (2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假

  600. */

  601. public static boolean isAVLRec(TreeNode root) {

  602. if(root == null){ // 如果二叉树为空,返回真

  603. return true;

  604. }

  605. // 如果左子树和右子树高度相差大于1,则非平衡二叉树, getDepthRec()是前面实现过的求树高度的方法

  606. if(Math.abs(getDepthRec(root.left) - getDepthRec(root.right)) > 1){

  607. return false;

  608. }

  609. // 递归判断左子树和右子树是否为平衡二叉树

  610. return isAVLRec(root.left) && isAVLRec(root.right);

  611. }

  612. /**

  613. * 求二叉树的镜像 递归解法:

  614. * (1)如果二叉树为空,返回空

  615. * (2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树

  616. */

  617. // 1. 破坏原来的树,把原来的树改成其镜像

  618. public static TreeNode mirrorRec(TreeNode root) {

  619. if (root == null) {

  620. return null;

  621. }

  622. TreeNode left = mirrorRec(root.left);

  623. TreeNode right = mirrorRec(root.right);

  624. root.left = right;

  625. root.right = left;

  626. return root;

  627. }

  628. // 2. 不能破坏原来的树,返回一个新的镜像树

  629. public static TreeNode mirrorCopyRec(TreeNode root){

  630. if(root == null){

  631. return null;

  632. }

  633. TreeNode newNode = new TreeNode(root.val);

  634. newNode.left = mirrorCopyRec(root.right);

  635. newNode.right = mirrorCopyRec(root.left);

  636. return newNode;

  637. }

  638. // 3. 判断两个树是否互相镜像

  639. public static boolean isMirrorRec(TreeNode r1, TreeNode r2){

  640. // 如果两个树都是空树,则返回true

  641. if(r1==null && r2==null){

  642. return true;

  643. }

  644. // 如果有一棵树是空树,另一颗不是,则返回false

  645. if(r1==null || r2==null){

  646. return false;

  647. }

  648. // 如果两个树都非空树,则先比较根节点

  649. if(r1.val != r2.val){

  650. return false;

  651. }

  652. // 递归比较r1的左子树的镜像是不是r2右子树 和

  653. // r1的右子树的镜像是不是r2左子树

  654. return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);

  655. }

  656. // 1. 破坏原来的树,把原来的树改成其镜像

  657. public static void mirror(TreeNode root) {

  658. if(root == null){

  659. return;

  660. }

  661. Stack<TreeNode> stack = new Stack<TreeNode>();

  662. stack.push(root);

  663. while( !stack.isEmpty() ){

  664. TreeNode cur = stack.pop();

  665. // 交换左右孩子

  666. TreeNode tmp = cur.right;

  667. cur.right = cur.left;

  668. cur.left = tmp;

  669. if(cur.right != null){

  670. stack.push(cur.right);

  671. }

  672. if(cur.left != null){

  673. stack.push(cur.left);

  674. }

  675. }

  676. }

  677. // 2. 不能破坏原来的树,返回一个新的镜像树

  678. public static TreeNode mirrorCopy(TreeNode root){

  679. if(root == null){

  680. return null;

  681. }

  682. Stack<TreeNode> stack = new Stack<TreeNode>();

  683. Stack<TreeNode> newStack = new Stack<TreeNode>();

  684. stack.push(root);

  685. TreeNode newRoot = new TreeNode(root.val);

  686. newStack.push(newRoot);

  687. while( !stack.isEmpty() ){

  688. TreeNode cur = stack.pop();

  689. TreeNode newCur = newStack.pop();

  690. if(cur.right != null){

  691. stack.push(cur.right);

  692. newCur.left = new TreeNode(cur.right.val);

  693. newStack.push(newCur.left);

  694. }

  695. if(cur.left != null){

  696. stack.push(cur.left);

  697. newCur.right = new TreeNode(cur.left.val);

  698. newStack.push(newCur.right);

  699. }

  700. }

  701. return newRoot;

  702. }

  703. /**

  704. * 求二叉树中两个节点的最低公共祖先节点

  705. * 递归解法:

  706. * (1)如果两个节点分别在根节点的左子树和右子树,则返回根节点

  707. * (2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树

  708. */

  709. public static TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {

  710. if (findNodeRec(root.left, n1)) { // 如果n1在树的左子树

  711. if (findNodeRec(root.right, n2)) { // 如果n2在树的右子树

  712. return root; // 返回根节点

  713. } else { // 如果n2也在树的左子树

  714. return getLastCommonParentRec(root.left, n1, n2); // 递归处理

  715. }

  716. } else { // 如果n1在树的右子树

  717. if (findNodeRec(root.left, n2)) { // 如果n2在左子树

  718. return root;

  719. } else { // 如果n2在右子树

  720. return getLastCommonParentRec(root.right, n1, n2); // 递归处理

  721. }

  722. }

  723. }

  724. // 帮助方法,递归判断一个点是否在树里

  725. private static boolean findNodeRec(TreeNode root, TreeNode node) {

  726. if (root == null || node == null) {

  727. return false;

  728. }

  729. if (root == node) {

  730. return true;

  731. }

  732. // 先尝试在左子树中查找

  733. boolean found = findNodeRec(root.left, node);

  734. if (!found) { // 如果查找不到,再在右子树中查找

  735. found = findNodeRec(root.right, node);

  736. }

  737. return found;

  738. }

  739. // 求二叉树中两个节点的最低公共祖先节点 (更加简洁版的递归)

  740. public static TreeNode getLastCommonParentRec2(TreeNode root, TreeNode n1, TreeNode n2) {

  741. if(root == null){

  742. return null;

  743. }

  744. // 如果有一个match,则说明当前node就是要找的最低公共祖先

  745. if(root.equals(n1) || root.equals(n2)){

  746. return root;

  747. }

  748. TreeNode commonInLeft = getLastCommonParentRec2(root.left, n1, n2);

  749. TreeNode commonInRight = getLastCommonParentRec2(root.right, n1, n2);

  750. // 如果一个左子树找到,一个在右子树找到,则说明root是唯一可能的最低公共祖先

  751. if(commonInLeft!=null && commonInRight!=null){

  752. return root;

  753. }

  754. // 其他情况是要不然在左子树要不然在右子树

  755. if(commonInLeft != null){

  756. return commonInLeft;

  757. }

  758. return commonInRight;

  759. }

  760. /**

  761. * 非递归解法:

  762. * 先求从根节点到两个节点的路径,然后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点

  763. */

  764. public static TreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2) {

  765. if (root == null || n1 == null || n2 == null) {

  766. return null;

  767. }

  768. ArrayList<TreeNode> p1 = new ArrayList<TreeNode>();

  769. boolean res1 = getNodePath(root, n1, p1);

  770. ArrayList<TreeNode> p2 = new ArrayList<TreeNode>();

  771. boolean res2 = getNodePath(root, n2, p2);

  772. if (!res1 || !res2) {

  773. return null;

  774. }

  775. TreeNode last = null;

  776. Iterator<TreeNode> iter1 = p1.iterator();

  777. Iterator<TreeNode> iter2 = p2.iterator();

  778. while (iter1.hasNext() && iter2.hasNext()) {

  779. TreeNode tmp1 = iter1.next();

  780. TreeNode tmp2 = iter2.next();

  781. if (tmp1 == tmp2) {

  782. last = tmp1;

  783. } else { // 直到遇到非公共节点

  784. break;

  785. }

  786. }

  787. return last;

  788. }

  789. // 把从根节点到node路径上所有的点都添加到path中

  790. private static boolean getNodePath(TreeNode root, TreeNode node, ArrayList<TreeNode> path) {

  791. if (root == null) {

  792. return false;

  793. }

  794. path.add(root); // 把这个节点加到路径中

  795. if (root == node) {

  796. return true;

  797. }

  798. boolean found = false;

  799. found = getNodePath(root.left, node, path); // 先在左子树中找

  800. if (!found) { // 如果没找到,再在右子树找

  801. found = getNodePath(root.right, node, path);

  802. }

  803. if (!found) { // 如果实在没找到证明这个节点不在路径中,说明刚才添加进去的不是路径上的节点,删掉!

  804. path.remove(root);

  805. }

  806. return found;

  807. }

  808. /**

  809. * 求二叉树中节点的最大距离 即二叉树中相距最远的两个节点之间的距离。 (distance / diameter)

  810. * 递归解法:

  811. * (1)如果二叉树为空,返回0,同时记录左子树和右子树的深度,都为0

  812. * (2)如果二叉树不为空,最大距离要么是左子树中的最大距离,要么是右子树中的最大距离,

  813. * 要么是左子树节点中到根节点的最大距离+右子树节点中到根节点的最大距离,

  814. * 同时记录左子树和右子树节点中到根节点的最大距离。

  815. *

  816. * http://www.cnblogs.com/miloyip/archive/2010/02/25/1673114.html

  817. *

  818. * 计算一个二叉树的最大距离有两个情况:

  819. 情况A: 路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。

  820. 情况B: 路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。

  821. 只需要计算这两个情况的路径距离,并取其大者,就是该二叉树的最大距离

  822. */

  823. public static Result getMaxDistanceRec(TreeNode root){

  824. if(root == null){

  825. Result empty = new Result(0, -1); // 目的是让调用方 +1 后,把当前的不存在的 (NULL) 子树当成最大深度为 0

  826. return empty;

  827. }

  828. // 计算出左右子树分别最大距离

  829. Result lmd = getMaxDistanceRec(root.left);

  830. Result rmd = getMaxDistanceRec(root.right);

  831. Result res = new Result();

  832. res.maxDepth = Math.max(lmd.maxDepth, rmd.maxDepth) + 1; // 当前最大深度

  833. // 取情况A和情况B中较大值

  834. res.maxDistance = Math.max( lmd.maxDepth+rmd.maxDepth, Math.max(lmd.maxDistance, rmd.maxDistance) );

  835. return res;

  836. }

  837. private static class Result{

  838. int maxDistance;

  839. int maxDepth;

  840. public Result() {

  841. }

  842. public Result(int maxDistance, int maxDepth) {

  843. this.maxDistance = maxDistance;

  844. this.maxDepth = maxDepth;

  845. }

  846. }

  847. /**

  848. * 13. 由前序遍历序列和中序遍历序列重建二叉树(递归)

  849. * 感觉这篇是讲的最为清晰的:

  850. * http://crackinterviewtoday.wordpress.com/2010/03/15/rebuild-a-binary-tree-from-inorder-and-preorder-traversals/

  851. * 文中还提到一种避免开额外空间的方法,等下次补上

  852. */

  853. public static TreeNode rebuildBinaryTreeRec(List<Integer> preOrder, List<Integer> inOrder){

  854. TreeNode root = null;

  855. List<Integer> leftPreOrder;

  856. List<Integer> rightPreOrder;

  857. List<Integer> leftInorder;

  858. List<Integer> rightInorder;

  859. int inorderPos;

  860. int preorderPos;

  861. if ((preOrder.size() != 0) && (inOrder.size() != 0))

  862. {

  863. // 把preorder的第一个元素作为root

  864. root = new TreeNode(preOrder.get(0));

  865. // Based upon the current node data seperate the traversals into leftPreorder, rightPreorder,

  866. // leftInorder, rightInorder lists

  867. // 因为知道root节点了,所以根据root节点位置,把preorder,inorder分别划分为 root左侧 和 右侧 的两个子区间

  868. inorderPos = inOrder.indexOf(preOrder.get(0)); // inorder序列的分割点

  869. leftInorder = inOrder.subList(0, inorderPos);

  870. rightInorder = inOrder.subList(inorderPos + 1, inOrder.size());

  871. preorderPos = leftInorder.size(); // preorder序列的分割点

  872. leftPreOrder = preOrder.subList(1, preorderPos + 1);

  873. rightPreOrder = preOrder.subList(preorderPos + 1, preOrder.size());

  874. root.left = rebuildBinaryTreeRec(leftPreOrder, leftInorder); // root的左子树就是preorder和inorder的左侧区间而形成的树

  875. root.right = rebuildBinaryTreeRec(rightPreOrder, rightInorder); // root的右子树就是preorder和inorder的右侧区间而形成的树

  876. }

  877. return root;

  878. }

  879. /**

  880. 14. 判断二叉树是不是完全二叉树(迭代)

  881. 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,

  882. 第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

  883. 有如下算法,按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时,

  884. 则该节点右子树必须为空,且后面遍历的节点左右子树都必须为空,否则不是完全二叉树。

  885. */

  886. public static boolean isCompleteBinaryTree(TreeNode root){

  887. if(root == null){

  888. return false;

  889. }

  890. Queue<TreeNode> queue = new LinkedList<TreeNode>();

  891. queue.add(root);

  892. boolean mustHaveNoChild = false;

  893. boolean result = true;

  894. while( !queue.isEmpty() ){

  895. TreeNode cur = queue.remove();

  896. if(mustHaveNoChild){ // 已经出现了有空子树的节点了,后面出现的必须为叶节点(左右子树都为空)

  897. if(cur.left!=null || cur.right!=null){

  898. result = false;

  899. break;

  900. }

  901. } else {

  902. if(cur.left!=null && cur.right!=null){ // 如果左子树和右子树都非空,则继续遍历

  903. queue.add(cur.left);

  904. queue.add(cur.right);

  905. }else if(cur.left!=null && cur.right==null){ // 如果左子树非空但右子树为空,说明已经出现空节点,之后必须都为空子树

  906. mustHaveNoChild = true;

  907. queue.add(cur.left);

  908. }else if(cur.left==null && cur.right!=null){ // 如果左子树为空但右子树非空,说明这棵树已经不是完全二叉完全树!

  909. result = false;

  910. break;

  911. }else{ // 如果左右子树都为空,则后面的必须也都为空子树

  912. mustHaveNoChild = true;

  913. }

  914. }

  915. }

  916. return result;

  917. }

  918. /**

  919. * 14. 判断二叉树是不是完全二叉树(递归)

  920. * http://stackoverflow.com/questions/1442674/how-to-determine-whether-a-binary-tree-is-complete

  921. *

  922. */

  923. public static boolean isCompleteBinaryTreeRec(TreeNode root){

  924. // Pair notComplete = new Pair(-1, false);

  925. // return !isCompleteBinaryTreeSubRec(root).equalsTo(notComplete);

  926. return isCompleteBinaryTreeSubRec(root).height != -1;

  927. }

  928. // 递归判断是否满树(完美)

  929. public static boolean isPerfectBinaryTreeRec(TreeNode root){

  930. return isCompleteBinaryTreeSubRec(root).isFull;

  931. }

  932. // 递归,要创建一个Pair class来保存树的高度和是否已满的信息

  933. public static Pair isCompleteBinaryTreeSubRec(TreeNode root){

  934. if(root == null){

  935. return new Pair(0, true);

  936. }

  937. Pair left = isCompleteBinaryTreeSubRec(root.left);

  938. Pair right = isCompleteBinaryTreeSubRec(root.right);

  939. // 左树满节点,而且左右树相同高度,则是唯一可能形成满树(若右树也是满节点)的情况

  940. if(left.isFull && left.height==right.height){

  941. return new Pair(1+left.height, right.isFull);

  942. }

  943. // 左树非满,但右树是满节点,且左树高度比右树高一

  944. // 注意到如果其左树为非完全树,则它的高度已经被设置成-1,

  945. // 因此不可能满足第二个条件!

  946. if(right.isFull && left.height==right.height+1){

  947. return new Pair(1+left.height, false);

  948. }

  949. // 其他情况都是非完全树,直接设置高度为-1

  950. return new Pair(-1, false);

  951. }

  952. private static class Pair{

  953. int height; // 树的高度

  954. boolean isFull; // 是否是个满树

  955. public Pair(int height, boolean isFull) {

  956. this.height = height;

  957. this.isFull = isFull;

  958. }

  959. public boolean equalsTo(Pair obj){

  960. return this.height==obj.height && this.isFull==obj.isFull;

  961. }

  962. }

  963. }

二叉树总结挺好的很好记忆相关推荐

  1. 数据结构——树与二叉树

    树与二叉树 一.树的定义: 1.定义:树(Tree)是n(n>=0)个节点的有限集,n=0时称为"空树".在任意一棵非空树中: ⒈有且仅有一个特定的称为根(root)的节点. ...

  2. 二叉树2 - 数据结构和算法44

    二叉树2 让编程改变世界 Change the world by program 二叉树的性质 二叉树的性质一:在二叉树的第i层上至多有2^(i-1)个结点(i>=1) 这个性质其实很好记忆,考 ...

  3. 数据结构(C++)笔记:05.树和二叉树

    文章目录 5.1 树的逻辑结构 5.1.1 树的定义和基本术语 5.1.2 树的抽象数据类型定义 5.1.3 树的遍历操作 5.2 树的存储结构 5.2.1 双亲表示法 5.2.2 孩子表示法 5.2 ...

  4. 各位家长非常辛苦,其他老师也很辛苦,孩子也很辛苦。希望我们相互理解,用朋友的角度去商量让孩子变的好起来...

    各位家长非常辛苦,其他老师也很辛苦,孩子也很辛苦.希望我们相互理解,用朋友的角度去商量让孩子变的好起来 2011年03月04日 一些教育中的心得. 做为一个普通人,机缘巧合站在三尺讲台,结合当年海外求 ...

  5. Java数据结构——认识二叉树

    作者:敲代码の流川枫 博客主页:流川枫的博客 专栏:和我一起学java 语录:Stay hungry stay foolish 工欲善其事必先利其器,给大家介绍一款超牛的斩获大厂offer利器--牛客 ...

  6. 深度优先搜索之记忆化dfs

    文章目录 前言 朴素dfs的求解思路 记忆化dfs的求解思路 将整数按权重排序 题目描述 解题思路 朴素dfs 记忆化dfs 小结 矩阵中的最长递增路径 题目描述 解题思路 朴素dfs 记忆化dfs ...

  7. 很多人说单片机很简单,有些本专业学生为什么学起来这么吃力?

    大家好,我是无际单片机编程的徐工. 在网上看到这么一个话题,自己特别有感触,不自觉的想写一下自己的看法. 单片机编程,我们的教材是<单片机原理及应用>. 当时我们的很多同学都觉得单片是最难 ...

  8. 二十五、二叉树的前序、中序、后序遍历

    一.为何使用树这种数据结构 数组存储方式的分析 优点:通过下标方式访问元素,速度快.对于有序数组,还可使用二分查找提高检索速度. 缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较 ...

  9. 二叉树c语言程序插入某个成员,关于C ++:二叉树:插入节点算法

    我正在尝试实现二叉树(如果它是普通的二叉树或二叉搜索树,则不重要),并且我在创建节点并将其链接到树的功能上遇到了一些麻烦. 这是我到目前为止编写的代码: class BinaryTree { clas ...

最新文章

  1. RabbitMQ 如何消息生产者producer发送给RabbitMQ服务器broker?
  2. JAVA各种并发锁从synchronized 到CAS 到 AQS
  3. PL/SQL 操作数据库常见脚本
  4. 陈桥五笔用户编号怎么获取_委托书中“样品原编号”怎么填?
  5. Dataset:数据生成之利用pandas自定义生成随机各自类型(离散型和连续型)的dataframe数据
  6. Redhat 5.4 安装Vbox 增强工具失败解决方法。
  7. 深入理解计算机系统第四版_深入理解计算机系统之存储器层次结构
  8. Linux信号实践(2) --信号分类
  9. IDEA中修改自动生成的Servlet模板,提高编码效率
  10. Pytorch:比较函数
  11. Retrofit的使用教程(二)
  12. iPhone 记录之 点与像素
  13. 方差分析表和回归分析表的那些浆糊糊
  14. POJ 有关动态规划的题目
  15. Oracle AutoVue 安装与配置说明
  16. 小米Pro 安装Ubuntu,以及安装成功之后,各种关机重启的卡死问题
  17. BugkuCTF中套路满满的题--------never give up
  18. ABP微服务示例中ProductManagementHttpApiClient服务地址配置
  19. 给大家分享一个好的短信接口.
  20. 26篇计量经济经典论文复现数据和Stata或R代码

热门文章

  1. 2019-03-15-算法-进化(有效的数独)
  2. P5641 【CSGRound2】开拓者的卓识(多项式)
  3. CF938G Shortest Path Queries(线性基/线段树分治/异或)
  4. 约会安排 HDU - 4553
  5. 牛客题霸 [判断一个链表是否为回文结构] C++题解/答案
  6. 不止代码:ybtoj-消除木块(区间DP)
  7. ARC122C-Calculator【乱搞,构造】
  8. CF235C-Cyclical Quest【SAM】
  9. jzoj4226-A【图论】
  10. ssl提高组周六备考赛【2018.10.27】