- package org.hunan.guan.javaIO;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Queue;
- import java.util.Stack;
- /**
- * http://blog.youkuaiyun.com/luckyxiaoqiang/article/details/7518888 轻松搞定面试中的二叉树题目
- * http://www.cnblogs.com/Jax/archive/2009/12/28/1633691.html 算法大全(3) 二叉树
- *
- * TODO: 一定要能熟练地写出所有问题的递归和非递归做法!
- *
- * 1. 求二叉树中的节点个数: getNodeNumRec(递归),getNodeNum(迭代)
- * 2. 求二叉树的深度: getDepthRec(递归),getDepth
- * 3. 前序遍历,中序遍历,后序遍历: preorderTraversalRec, preorderTraversal, inorderTraversalRec, postorderTraversalRec
- * (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2)
- * 4.分层遍历二叉树(按层次从上往下,从左往右): levelTraversal, levelTraversalRec(递归解法!)
- * 5. 将二叉查找树变为有序的双向链表: convertBST2DLLRec, convertBST2DLL
- * 6. 求二叉树第K层的节点个数:getNodeNumKthLevelRec, getNodeNumKthLevel
- * 7. 求二叉树中叶子节点的个数:getNodeNumLeafRec, getNodeNumLeaf
- * 8. 判断两棵二叉树是否相同的树:isSameRec, isSame
- * 9. 判断二叉树是不是平衡二叉树:isAVLRec
- * 10. 求二叉树的镜像(破坏和不破坏原来的树两种情况):mirrorRec, mirrorCopyRec
- * 10.1 判断两个树是否互相镜像:isMirrorRec
- * 11. 求二叉树中两个节点的最低公共祖先节点:getLastCommonParent, getLastCommonParentRec, getLastCommonParentRec2
- * 12. 求二叉树中节点的最大距离:getMaxDistanceRec
- * 13. 由前序遍历序列和中序遍历序列重建二叉树:rebuildBinaryTreeRec
- * 14.判断二叉树是不是完全二叉树:isCompleteBinaryTree, isCompleteBinaryTreeRec
- *
- */
- public class BinaryTreeSummary {
- /*
- 1
- / \
- 2 3
- / \ \
- 4 5 6
- */
- public static void main(String[] args) {
- TreeNode r1 = new TreeNode(1);
- TreeNode r2 = new TreeNode(2);
- TreeNode r3 = new TreeNode(3);
- TreeNode r4 = new TreeNode(4);
- TreeNode r5 = new TreeNode(5);
- TreeNode r6 = new TreeNode(6);
- r1.left = r2;
- r1.right = r3;
- r2.left = r4;
- r2.right = r5;
- r3.right = r6;
- // System.out.println(getNodeNumRec(r1));
- // System.out.println(getNodeNum(r1));
- // System.out.println(getDepthRec(r1));
- // System.out.println(getDepth(r1));
- // preorderTraversalRec(r1);
- // System.out.println();
- // preorderTraversal(r1);
- // System.out.println();
- // inorderTraversalRec(r1);
- // System.out.println();
- // inorderTraversal(r1);
- // System.out.println();
- // postorderTraversalRec(r1);
- // System.out.println();
- // postorderTraversal(r1);
- // System.out.println();
- // levelTraversal(r1);
- // System.out.println();
- // levelTraversalRec(r1);
- // System.out.println();
- // TreeNode tmp = convertBSTRec(r1);
- // while(true){
- // if(tmp == null){
- // break;
- // }
- // System.out.print(tmp.val + " ");
- // if(tmp.right == null){
- // break;
- // }
- // tmp = tmp.right;
- // }
- // System.out.println();
- // while(true){
- // if(tmp == null){
- // break;
- // }
- // System.out.print(tmp.val + " ");
- // if(tmp.left == null){
- // break;
- // }
- // tmp = tmp.left;
- // }
- // TreeNode tmp = convertBST2DLL(r1);
- // while(true){
- // if(tmp == null){
- // break;
- // }
- // System.out.print(tmp.val + " ");
- // if(tmp.right == null){
- // break;
- // }
- // tmp = tmp.right;
- // }
- // System.out.println(getNodeNumKthLevelRec(r1, 2));
- // System.out.println(getNodeNumKthLevel(r1, 2));
- // System.out.println(getNodeNumLeafRec(r1));
- // System.out.println(getNodeNumLeaf(r1));
- // System.out.println(isSame(r1, r1));
- // inorderTraversal(r1);
- // System.out.println();
- // mirror(r1);
- // TreeNode mirrorRoot = mirrorCopy(r1);
- // inorderTraversal(mirrorRoot);
- System.out.println(isCompleteBinaryTree(r1));
- System.out.println(isCompleteBinaryTreeRec(r1));
- }
- private static class TreeNode {
- int val;
- TreeNode left;
- TreeNode right;
- public TreeNode(int val) {
- this.val = val;
- }
- }
- /**
- * 求二叉树中的节点个数递归解法: O(n)
- * (1)如果二叉树为空,节点个数为0
- * (2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 +
- * 右子树节点个数 + 1
- */
- public static int getNodeNumRec(TreeNode root) {
- if (root == null) {
- return 0;
- } else {
- return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;
- }
- }
- /**
- * 求二叉树中的节点个数迭代解法O(n):基本思想同LevelOrderTraversal,
- * 即用一个Queue,在Java里面可以用LinkedList来模拟
- */
- public static int getNodeNum(TreeNode root) {
- if(root == null){
- return 0;
- }
- int count = 1;
- Queue<TreeNode> queue = new LinkedList<TreeNode>();
- queue.add(root);
- while(!queue.isEmpty()){
- TreeNode cur = queue.remove(); // 从队头位置移除
- if(cur.left != null){ // 如果有左孩子,加到队尾
- queue.add(cur.left);
- count++;
- }
- if(cur.right != null){ // 如果有右孩子,加到队尾
- queue.add(cur.right);
- count++;
- }
- }
- return count;
- }
- /**
- * 求二叉树的深度(高度) 递归解法: O(n)
- * (1)如果二叉树为空,二叉树的深度为0
- * (2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
- */
- public static int getDepthRec(TreeNode root) {
- if (root == null) {
- return 0;
- }
- int leftDepth = getDepthRec(root.left);
- int rightDepth = getDepthRec(root.right);
- return Math.max(leftDepth, rightDepth) + 1;
- }
- /**
- * 求二叉树的深度(高度) 迭代解法: O(n)
- * 基本思想同LevelOrderTraversal,还是用一个Queue
- */
- public static int getDepth(TreeNode root) {
- if(root == null){
- return 0;
- }
- int depth = 0; // 深度
- int currentLevelNodes = 1; // 当前Level,node的数量
- int nextLevelNodes = 0; // 下一层Level,node的数量
- LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
- queue.add(root);
- while( !queue.isEmpty() ){
- TreeNode cur = queue.remove(); // 从队头位置移除
- currentLevelNodes--; // 减少当前Level node的数量
- if(cur.left != null){ // 如果有左孩子,加到队尾
- queue.add(cur.left);
- nextLevelNodes++; // 并增加下一层Level node的数量
- }
- if(cur.right != null){ // 如果有右孩子,加到队尾
- queue.add(cur.right);
- nextLevelNodes++;
- }
- if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点
- depth++; // 增加高度
- currentLevelNodes = nextLevelNodes; // 初始化下一层的遍历
- nextLevelNodes = 0;
- }
- }
- return depth;
- }
- /**
- * 前序遍历,中序遍历,后序遍历 前序遍历递归解法:
- * (1)如果二叉树为空,空操作
- * (2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树
- */
- public static void preorderTraversalRec(TreeNode root) {
- if (root == null) {
- return;
- }
- System.out.print(root.val + " ");
- preorderTraversalRec(root.left);
- preorderTraversalRec(root.right);
- }
- /**
- * 前序遍历迭代解法:用一个辅助stack,总是把右孩子放进栈
- * http://www.youtube.com/watch?v=uPTCbdHSFg4
- */
- public static void preorderTraversal(TreeNode root) {
- if(root == null){
- return;
- }
- Stack<TreeNode> stack = new Stack<TreeNode>(); // 辅助stack
- stack.push(root);
- while( !stack.isEmpty() ){
- TreeNode cur = stack.pop(); // 出栈栈顶元素
- System.out.print(cur.val + " ");
- // 关键点:要先压入右孩子,再压入左孩子,这样在出栈时会先打印左孩子再打印右孩子
- if(cur.right != null){
- stack.push(cur.right);
- }
- if(cur.left != null){
- stack.push(cur.left);
- }
- }
- }
- /**
- * 中序遍历递归解法
- * (1)如果二叉树为空,空操作。
- * (2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树
- */
- public static void inorderTraversalRec(TreeNode root) {
- if (root == null) {
- return;
- }
- inorderTraversalRec(root.left);
- System.out.print(root.val + " ");
- inorderTraversalRec(root.right);
- }
- /**
- * 中序遍历迭代解法 ,用栈先把根节点的所有左孩子都添加到栈内,
- * 然后输出栈顶元素,再处理栈顶元素的右子树
- * http://www.youtube.com/watch?v=50v1sJkjxoc
- *
- * 还有一种方法能不用递归和栈,基于线索二叉树的方法,较麻烦以后补上
- * http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/
- */
- public static void inorderTraversal(TreeNode root){
- if(root == null){
- return;
- }
- Stack<TreeNode> stack = new Stack<TreeNode>();
- TreeNode cur = root;
- while( true ){
- while(cur != null){ // 先添加一个非空节点所有的左孩子到栈
- stack.push(cur);
- cur = cur.left;
- }
- if(stack.isEmpty()){
- break;
- }
- // 因为此时已经没有左孩子了,所以输出栈顶元素
- cur = stack.pop();
- System.out.print(cur.val + " ");
- cur = cur.right; // 准备处理右子树
- }
- }
- /**
- * 后序遍历递归解法
- * (1)如果二叉树为空,空操作
- * (2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点
- */
- public static void postorderTraversalRec(TreeNode root) {
- if (root == null) {
- return;
- }
- postorderTraversalRec(root.left);
- postorderTraversalRec(root.right);
- System.out.print(root.val + " ");
- }
- /**
- * 后序遍历迭代解法
- * http://www.youtube.com/watch?v=hv-mJUs5mvU
- *
- */
- public static void postorderTraversal(TreeNode root) {
- if (root == null) {
- return;
- }
- Stack<TreeNode> s = new Stack<TreeNode>(); // 第一个stack用于添加node和它的左右孩子
- Stack<TreeNode> output = new Stack<TreeNode>();// 第二个stack用于翻转第一个stack输出
- s.push(root);
- while( !s.isEmpty() ){ // 确保所有元素都被翻转转移到第二个stack
- TreeNode cur = s.pop(); // 把栈顶元素添加到第二个stack
- output.push(cur);
- if(cur.left != null){ // 把栈顶元素的左孩子和右孩子分别添加入第一个stack
- s.push(cur.left);
- }
- if(cur.right != null){
- s.push(cur.right);
- }
- }
- while( !output.isEmpty() ){ // 遍历输出第二个stack,即为后序遍历
- System.out.print(output.pop().val + " ");
- }
- }
- /**
- * 分层遍历二叉树(按层次从上往下,从左往右)迭代
- * 相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点
- * ,访问,若左子节点或右子节点不为空,将其压入队列
- */
- public static void levelTraversal(TreeNode root) {
- if (root == null) {
- return;
- }
- LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
- queue.push(root);
- while (!queue.isEmpty()) {
- TreeNode cur = queue.removeFirst();
- System.out.print(cur.val + " ");
- if (cur.left != null) {
- queue.add(cur.left);
- }
- if (cur.right != null) {
- queue.add(cur.right);
- }
- }
- }
- /**
- * 分层遍历二叉树(递归)
- * 很少有人会用递归去做level traversal
- * 基本思想是用一个大的ArrayList,里面包含了每一层的ArrayList。
- * 大的ArrayList的size和level有关系
- *
- * 这是我目前见到的最好的递归解法!
- * http://discuss.leetcode.com/questions/49/binary-tree-level-order-traversal#answer-container-2543
- */
- public static void levelTraversalRec(TreeNode root) {
- ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();
- dfs(root, 0, ret);
- System.out.println(ret);
- }
- private static void dfs(TreeNode root, int level, ArrayList<ArrayList<Integer>> ret){
- if(root == null){
- return;
- }
- // 添加一个新的ArrayList表示新的一层
- if(level >= ret.size()){
- ret.add(new ArrayList<Integer>());
- }
- ret.get(level).add(root.val); // 把节点添加到表示那一层的ArrayList里
- dfs(root.left, level+1, ret); // 递归处理下一层的左子树和右子树
- dfs(root.right, level+1, ret);
- }
- /**
- * 将二叉查找树变为有序的双向链表 要求不能创建新节点,只调整指针。
- * 递归解法:
- * 参考了http://stackoverflow.com/questions/11511898/converting-a-binary-search-tree-to-doubly-linked-list#answer-11530016
- * 感觉是最清晰的递归解法,但要注意递归完,root会在链表的中间位置,因此要手动
- * 把root移到链表头或链表尾
- */
- public static TreeNode convertBST2DLLRec(TreeNode root) {
- root = convertBST2DLLSubRec(root);
- // root会在链表的中间位置,因此要手动把root移到链表头
- while(root.left != null){
- root = root.left;
- }
- return root;
- }
- /**
- * 递归转换BST为双向链表(DLL)
- */
- public static TreeNode convertBST2DLLSubRec(TreeNode root){
- if(root==null || (root.left==null && root.right==null)){
- return root;
- }
- TreeNode tmp = null;
- if(root.left != null){ // 处理左子树
- tmp = convertBST2DLLSubRec(root.left);
- while(tmp.right != null){ // 寻找最右节点
- tmp = tmp.right;
- }
- tmp.right = root; // 把左子树处理后结果和root连接
- root.left = tmp;
- }
- if(root.right != null){ // 处理右子树
- tmp = convertBST2DLLSubRec(root.right);
- while(tmp.left != null){ // 寻找最左节点
- tmp = tmp.left;
- }
- tmp.left = root; // 把右子树处理后结果和root连接
- root.right = tmp;
- }
- return root;
- }
- /**
- * 将二叉查找树变为有序的双向链表 迭代解法
- // * 类似inorder traversal的做法
- */
- public static TreeNode convertBST2DLL(TreeNode root) {
- if(root == null){
- return null;
- }
- Stack<TreeNode> stack = new Stack<TreeNode>();
- TreeNode cur = root; // 指向当前处理节点
- TreeNode old = null; // 指向前一个处理的节点
- TreeNode head = null; // 链表头
- while( true ){
- while(cur != null){ // 先添加一个非空节点所有的左孩子到栈
- stack.push(cur);
- cur = cur.left;
- }
- if(stack.isEmpty()){
- break;
- }
- // 因为此时已经没有左孩子了,所以输出栈顶元素
- cur = stack.pop();
- if(old != null){
- old.right = cur;
- }
- if(head == null){ // /第一个节点为双向链表头节点
- head = cur;
- }
- old = cur; // 更新old
- cur = cur.right; // 准备处理右子树
- }
- return head;
- }
- /**
- * 求二叉树第K层的节点个数 递归解法:
- * (1)如果二叉树为空或者k<1返回0
- * (2)如果二叉树不为空并且k==1,返回1
- * (3)如果二叉树不为空且k>1,返回root左子树中k-1层的节点个数与root右子树k-1层节点个数之和
- *
- * 求以root为根的k层节点数目 等价于 求以root左孩子为根的k-1层(因为少了root那一层)节点数目 加上
- * 以root右孩子为根的k-1层(因为少了root那一层)节点数目
- *
- * 所以遇到树,先把它拆成左子树和右子树,把问题降解
- *
- */
- public static int getNodeNumKthLevelRec(TreeNode root, int k) {
- if (root == null || k < 1) {
- return 0;
- }
- if (k == 1) {
- return 1;
- }
- int numLeft = getNodeNumKthLevelRec(root.left, k - 1); // 求root左子树的k-1层节点数
- int numRight = getNodeNumKthLevelRec(root.right, k - 1); // 求root右子树的k-1层节点数
- return numLeft + numRight;
- }
- /**
- * 求二叉树第K层的节点个数 迭代解法:
- * 同getDepth的迭代解法
- */
- public static int getNodeNumKthLevel(TreeNode root, int k){
- if(root == null){
- return 0;
- }
- Queue<TreeNode> queue = new LinkedList<TreeNode>();
- queue.add(root);
- int i = 1;
- int currentLevelNodes = 1; // 当前Level,node的数量
- int nextLevelNodes = 0; // 下一层Level,node的数量
- while( !queue.isEmpty() && i<k){
- TreeNode cur = queue.remove(); // 从队头位置移除
- currentLevelNodes--; // 减少当前Level node的数量
- if(cur.left != null){ // 如果有左孩子,加到队尾
- queue.add(cur.left);
- nextLevelNodes++; // 并增加下一层Level node的数量
- }
- if(cur.right != null){ // 如果有右孩子,加到队尾
- queue.add(cur.right);
- nextLevelNodes++;
- }
- if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点
- currentLevelNodes = nextLevelNodes; // 初始化下一层的遍历
- nextLevelNodes = 0;
- i++; // 进入到下一层
- }
- }
- return currentLevelNodes;
- }
- /**
- * 求二叉树中叶子节点的个数(递归)
- */
- public static int getNodeNumLeafRec(TreeNode root) {
- // 当root不存在,返回空
- if (root == null) {
- return 0;
- }
- // 当为叶子节点时返回1
- if (root.left == null && root.right == null) {
- return 1;
- }
- // 把一个树拆成左子树和右子树之和,原理同上一题
- return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);
- }
- /**
- * 求二叉树中叶子节点的个数(迭代)
- * 还是基于Level order traversal
- */
- public static int getNodeNumLeaf(TreeNode root) {
- if(root == null){
- return 0;
- }
- Queue<TreeNode> queue = new LinkedList<TreeNode>();
- queue.add(root);
- int leafNodes = 0; // 记录上一个Level,node的数量
- while( !queue.isEmpty() ){
- TreeNode cur = queue.remove(); // 从队头位置移除
- if(cur.left != null){ // 如果有左孩子,加到队尾
- queue.add(cur.left);
- }
- if(cur.right != null){ // 如果有右孩子,加到队尾
- queue.add(cur.right);
- }
- if(cur.left==null && cur.right==null){ // 叶子节点
- leafNodes++;
- }
- }
- return leafNodes;
- }
- /**
- * 判断两棵二叉树是否相同的树。
- * 递归解法:
- * (1)如果两棵二叉树都为空,返回真
- * (2)如果两棵二叉树一棵为空,另一棵不为空,返回假
- * (3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
- */
- public static boolean isSameRec(TreeNode r1, TreeNode r2) {
- // 如果两棵二叉树都为空,返回真
- if (r1 == null && r2 == null) {
- return true;
- }
- // 如果两棵二叉树一棵为空,另一棵不为空,返回假
- else if (r1 == null || r2 == null) {
- return false;
- }
- if(r1.val != r2.val){
- return false;
- }
- boolean leftRes = isSameRec(r1.left, r2.left); // 比较对应左子树
- boolean rightRes = isSameRec(r1.right, r2.right); // 比较对应右子树
- return leftRes && rightRes;
- }
- /**
- * 判断两棵二叉树是否相同的树(迭代)
- * 遍历一遍即可,这里用preorder
- */
- public static boolean isSame(TreeNode r1, TreeNode r2) {
- // 如果两个树都是空树,则返回true
- if(r1==null && r2==null){
- return true;
- }
- // 如果有一棵树是空树,另一颗不是,则返回false
- if(r1==null || r2==null){
- return false;
- }
- Stack<TreeNode> s1 = new Stack<TreeNode>();
- Stack<TreeNode> s2 = new Stack<TreeNode>();
- s1.push(r1);
- s2.push(r2);
- while(!s1.isEmpty() && !s2.isEmpty()){
- TreeNode n1 = s1.pop();
- TreeNode n2 = s2.pop();
- if(n1==null && n2==null){
- continue;
- }else if(n1!=null && n2!=null && n1.val==n2.val){
- s1.push(n1.right);
- s1.push(n1.left);
- s2.push(n2.right);
- s2.push(n2.left);
- }else{
- return false;
- }
- }
- return true;
- }
- /**
- * 判断二叉树是不是平衡二叉树 递归解法:
- * (1)如果二叉树为空,返回真
- * (2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假
- */
- public static boolean isAVLRec(TreeNode root) {
- if(root == null){ // 如果二叉树为空,返回真
- return true;
- }
- // 如果左子树和右子树高度相差大于1,则非平衡二叉树, getDepthRec()是前面实现过的求树高度的方法
- if(Math.abs(getDepthRec(root.left) - getDepthRec(root.right)) > 1){
- return false;
- }
- // 递归判断左子树和右子树是否为平衡二叉树
- return isAVLRec(root.left) && isAVLRec(root.right);
- }
- /**
- * 求二叉树的镜像 递归解法:
- * (1)如果二叉树为空,返回空
- * (2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树
- */
- // 1. 破坏原来的树,把原来的树改成其镜像
- public static TreeNode mirrorRec(TreeNode root) {
- if (root == null) {
- return null;
- }
- TreeNode left = mirrorRec(root.left);
- TreeNode right = mirrorRec(root.right);
- root.left = right;
- root.right = left;
- return root;
- }
- // 2. 不能破坏原来的树,返回一个新的镜像树
- public static TreeNode mirrorCopyRec(TreeNode root){
- if(root == null){
- return null;
- }
- TreeNode newNode = new TreeNode(root.val);
- newNode.left = mirrorCopyRec(root.right);
- newNode.right = mirrorCopyRec(root.left);
- return newNode;
- }
- // 3. 判断两个树是否互相镜像
- public static boolean isMirrorRec(TreeNode r1, TreeNode r2){
- // 如果两个树都是空树,则返回true
- if(r1==null && r2==null){
- return true;
- }
- // 如果有一棵树是空树,另一颗不是,则返回false
- if(r1==null || r2==null){
- return false;
- }
- // 如果两个树都非空树,则先比较根节点
- if(r1.val != r2.val){
- return false;
- }
- // 递归比较r1的左子树的镜像是不是r2右子树 和
- // r1的右子树的镜像是不是r2左子树
- return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);
- }
- // 1. 破坏原来的树,把原来的树改成其镜像
- public static void mirror(TreeNode root) {
- if(root == null){
- return;
- }
- Stack<TreeNode> stack = new Stack<TreeNode>();
- stack.push(root);
- while( !stack.isEmpty() ){
- TreeNode cur = stack.pop();
- // 交换左右孩子
- TreeNode tmp = cur.right;
- cur.right = cur.left;
- cur.left = tmp;
- if(cur.right != null){
- stack.push(cur.right);
- }
- if(cur.left != null){
- stack.push(cur.left);
- }
- }
- }
- // 2. 不能破坏原来的树,返回一个新的镜像树
- public static TreeNode mirrorCopy(TreeNode root){
- if(root == null){
- return null;
- }
- Stack<TreeNode> stack = new Stack<TreeNode>();
- Stack<TreeNode> newStack = new Stack<TreeNode>();
- stack.push(root);
- TreeNode newRoot = new TreeNode(root.val);
- newStack.push(newRoot);
- while( !stack.isEmpty() ){
- TreeNode cur = stack.pop();
- TreeNode newCur = newStack.pop();
- if(cur.right != null){
- stack.push(cur.right);
- newCur.left = new TreeNode(cur.right.val);
- newStack.push(newCur.left);
- }
- if(cur.left != null){
- stack.push(cur.left);
- newCur.right = new TreeNode(cur.left.val);
- newStack.push(newCur.right);
- }
- }
- return newRoot;
- }
- /**
- * 求二叉树中两个节点的最低公共祖先节点
- * 递归解法:
- * (1)如果两个节点分别在根节点的左子树和右子树,则返回根节点
- * (2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树
- */
- public static TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {
- if (findNodeRec(root.left, n1)) { // 如果n1在树的左子树
- if (findNodeRec(root.right, n2)) { // 如果n2在树的右子树
- return root; // 返回根节点
- } else { // 如果n2也在树的左子树
- return getLastCommonParentRec(root.left, n1, n2); // 递归处理
- }
- } else { // 如果n1在树的右子树
- if (findNodeRec(root.left, n2)) { // 如果n2在左子树
- return root;
- } else { // 如果n2在右子树
- return getLastCommonParentRec(root.right, n1, n2); // 递归处理
- }
- }
- }
- // 帮助方法,递归判断一个点是否在树里
- private static boolean findNodeRec(TreeNode root, TreeNode node) {
- if (root == null || node == null) {
- return false;
- }
- if (root == node) {
- return true;
- }
- // 先尝试在左子树中查找
- boolean found = findNodeRec(root.left, node);
- if (!found) { // 如果查找不到,再在右子树中查找
- found = findNodeRec(root.right, node);
- }
- return found;
- }
- // 求二叉树中两个节点的最低公共祖先节点 (更加简洁版的递归)
- public static TreeNode getLastCommonParentRec2(TreeNode root, TreeNode n1, TreeNode n2) {
- if(root == null){
- return null;
- }
- // 如果有一个match,则说明当前node就是要找的最低公共祖先
- if(root.equals(n1) || root.equals(n2)){
- return root;
- }
- TreeNode commonInLeft = getLastCommonParentRec2(root.left, n1, n2);
- TreeNode commonInRight = getLastCommonParentRec2(root.right, n1, n2);
- // 如果一个左子树找到,一个在右子树找到,则说明root是唯一可能的最低公共祖先
- if(commonInLeft!=null && commonInRight!=null){
- return root;
- }
- // 其他情况是要不然在左子树要不然在右子树
- if(commonInLeft != null){
- return commonInLeft;
- }
- return commonInRight;
- }
- /**
- * 非递归解法:
- * 先求从根节点到两个节点的路径,然后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点
- */
- public static TreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2) {
- if (root == null || n1 == null || n2 == null) {
- return null;
- }
- ArrayList<TreeNode> p1 = new ArrayList<TreeNode>();
- boolean res1 = getNodePath(root, n1, p1);
- ArrayList<TreeNode> p2 = new ArrayList<TreeNode>();
- boolean res2 = getNodePath(root, n2, p2);
- if (!res1 || !res2) {
- return null;
- }
- TreeNode last = null;
- Iterator<TreeNode> iter1 = p1.iterator();
- Iterator<TreeNode> iter2 = p2.iterator();
- while (iter1.hasNext() && iter2.hasNext()) {
- TreeNode tmp1 = iter1.next();
- TreeNode tmp2 = iter2.next();
- if (tmp1 == tmp2) {
- last = tmp1;
- } else { // 直到遇到非公共节点
- break;
- }
- }
- return last;
- }
- // 把从根节点到node路径上所有的点都添加到path中
- private static boolean getNodePath(TreeNode root, TreeNode node, ArrayList<TreeNode> path) {
- if (root == null) {
- return false;
- }
- path.add(root); // 把这个节点加到路径中
- if (root == node) {
- return true;
- }
- boolean found = false;
- found = getNodePath(root.left, node, path); // 先在左子树中找
- if (!found) { // 如果没找到,再在右子树找
- found = getNodePath(root.right, node, path);
- }
- if (!found) { // 如果实在没找到证明这个节点不在路径中,说明刚才添加进去的不是路径上的节点,删掉!
- path.remove(root);
- }
- return found;
- }
- /**
- * 求二叉树中节点的最大距离 即二叉树中相距最远的两个节点之间的距离。 (distance / diameter)
- * 递归解法:
- * (1)如果二叉树为空,返回0,同时记录左子树和右子树的深度,都为0
- * (2)如果二叉树不为空,最大距离要么是左子树中的最大距离,要么是右子树中的最大距离,
- * 要么是左子树节点中到根节点的最大距离+右子树节点中到根节点的最大距离,
- * 同时记录左子树和右子树节点中到根节点的最大距离。
- *
- * http://www.cnblogs.com/miloyip/archive/2010/02/25/1673114.html
- *
- * 计算一个二叉树的最大距离有两个情况:
- 情况A: 路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。
- 情况B: 路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。
- 只需要计算这两个情况的路径距离,并取其大者,就是该二叉树的最大距离
- */
- public static Result getMaxDistanceRec(TreeNode root){
- if(root == null){
- Result empty = new Result(0, -1); // 目的是让调用方 +1 后,把当前的不存在的 (NULL) 子树当成最大深度为 0
- return empty;
- }
- // 计算出左右子树分别最大距离
- Result lmd = getMaxDistanceRec(root.left);
- Result rmd = getMaxDistanceRec(root.right);
- Result res = new Result();
- res.maxDepth = Math.max(lmd.maxDepth, rmd.maxDepth) + 1; // 当前最大深度
- // 取情况A和情况B中较大值
- res.maxDistance = Math.max( lmd.maxDepth+rmd.maxDepth, Math.max(lmd.maxDistance, rmd.maxDistance) );
- return res;
- }
- private static class Result{
- int maxDistance;
- int maxDepth;
- public Result() {
- }
- public Result(int maxDistance, int maxDepth) {
- this.maxDistance = maxDistance;
- this.maxDepth = maxDepth;
- }
- }
- /**
- * 13. 由前序遍历序列和中序遍历序列重建二叉树(递归)
- * 感觉这篇是讲的最为清晰的:
- * http://crackinterviewtoday.wordpress.com/2010/03/15/rebuild-a-binary-tree-from-inorder-and-preorder-traversals/
- * 文中还提到一种避免开额外空间的方法,等下次补上
- */
- public static TreeNode rebuildBinaryTreeRec(List<Integer> preOrder, List<Integer> inOrder){
- TreeNode root = null;
- List<Integer> leftPreOrder;
- List<Integer> rightPreOrder;
- List<Integer> leftInorder;
- List<Integer> rightInorder;
- int inorderPos;
- int preorderPos;
- if ((preOrder.size() != 0) && (inOrder.size() != 0))
- {
- // 把preorder的第一个元素作为root
- root = new TreeNode(preOrder.get(0));
- // Based upon the current node data seperate the traversals into leftPreorder, rightPreorder,
- // leftInorder, rightInorder lists
- // 因为知道root节点了,所以根据root节点位置,把preorder,inorder分别划分为 root左侧 和 右侧 的两个子区间
- inorderPos = inOrder.indexOf(preOrder.get(0)); // inorder序列的分割点
- leftInorder = inOrder.subList(0, inorderPos);
- rightInorder = inOrder.subList(inorderPos + 1, inOrder.size());
- preorderPos = leftInorder.size(); // preorder序列的分割点
- leftPreOrder = preOrder.subList(1, preorderPos + 1);
- rightPreOrder = preOrder.subList(preorderPos + 1, preOrder.size());
- root.left = rebuildBinaryTreeRec(leftPreOrder, leftInorder); // root的左子树就是preorder和inorder的左侧区间而形成的树
- root.right = rebuildBinaryTreeRec(rightPreOrder, rightInorder); // root的右子树就是preorder和inorder的右侧区间而形成的树
- }
- return root;
- }
- /**
- 14. 判断二叉树是不是完全二叉树(迭代)
- 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,
- 第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
- 有如下算法,按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时,
- 则该节点右子树必须为空,且后面遍历的节点左右子树都必须为空,否则不是完全二叉树。
- */
- public static boolean isCompleteBinaryTree(TreeNode root){
- if(root == null){
- return false;
- }
- Queue<TreeNode> queue = new LinkedList<TreeNode>();
- queue.add(root);
- boolean mustHaveNoChild = false;
- boolean result = true;
- while( !queue.isEmpty() ){
- TreeNode cur = queue.remove();
- if(mustHaveNoChild){ // 已经出现了有空子树的节点了,后面出现的必须为叶节点(左右子树都为空)
- if(cur.left!=null || cur.right!=null){
- result = false;
- break;
- }
- } else {
- if(cur.left!=null && cur.right!=null){ // 如果左子树和右子树都非空,则继续遍历
- queue.add(cur.left);
- queue.add(cur.right);
- }else if(cur.left!=null && cur.right==null){ // 如果左子树非空但右子树为空,说明已经出现空节点,之后必须都为空子树
- mustHaveNoChild = true;
- queue.add(cur.left);
- }else if(cur.left==null && cur.right!=null){ // 如果左子树为空但右子树非空,说明这棵树已经不是完全二叉完全树!
- result = false;
- break;
- }else{ // 如果左右子树都为空,则后面的必须也都为空子树
- mustHaveNoChild = true;
- }
- }
- }
- return result;
- }
- /**
- * 14. 判断二叉树是不是完全二叉树(递归)
- * http://stackoverflow.com/questions/1442674/how-to-determine-whether-a-binary-tree-is-complete
- *
- */
- public static boolean isCompleteBinaryTreeRec(TreeNode root){
- // Pair notComplete = new Pair(-1, false);
- // return !isCompleteBinaryTreeSubRec(root).equalsTo(notComplete);
- return isCompleteBinaryTreeSubRec(root).height != -1;
- }
- // 递归判断是否满树(完美)
- public static boolean isPerfectBinaryTreeRec(TreeNode root){
- return isCompleteBinaryTreeSubRec(root).isFull;
- }
- // 递归,要创建一个Pair class来保存树的高度和是否已满的信息
- public static Pair isCompleteBinaryTreeSubRec(TreeNode root){
- if(root == null){
- return new Pair(0, true);
- }
- Pair left = isCompleteBinaryTreeSubRec(root.left);
- Pair right = isCompleteBinaryTreeSubRec(root.right);
- // 左树满节点,而且左右树相同高度,则是唯一可能形成满树(若右树也是满节点)的情况
- if(left.isFull && left.height==right.height){
- return new Pair(1+left.height, right.isFull);
- }
- // 左树非满,但右树是满节点,且左树高度比右树高一
- // 注意到如果其左树为非完全树,则它的高度已经被设置成-1,
- // 因此不可能满足第二个条件!
- if(right.isFull && left.height==right.height+1){
- return new Pair(1+left.height, false);
- }
- // 其他情况都是非完全树,直接设置高度为-1
- return new Pair(-1, false);
- }
- private static class Pair{
- int height; // 树的高度
- boolean isFull; // 是否是个满树
- public Pair(int height, boolean isFull) {
- this.height = height;
- this.isFull = isFull;
- }
- public boolean equalsTo(Pair obj){
- return this.height==obj.height && this.isFull==obj.isFull;
- }
- }
- }
相关推荐
常见的二叉树面试题大汇总(涵盖二叉搜索树)
常见的二叉树面试题大汇总(涵盖二叉搜索树)
二叉树相关代码题【较全】C语言
树的相关代码题 递归非递归、求高度、判断是否完全二叉树、相似二叉树、以及孩子兄弟表示法的相关例题求解
TLSv1.0与TLSv1.1安全漏洞深度解析与实战修复指南
本文深度解析TLSv1.0与TLSv1.1的安全漏洞,包括POODLE和BEAST攻击原理,并提供实战修复指南。通过禁用老旧协议、配置安全加密套件和网络层防护措施,有效提升系统安全性。文章还针对无法立即升级的场景,给出了补偿性防御方案,帮助管理员全面应对传输层安全风险。
java 二叉树面试_二叉树操作(面试必备)
本篇针对面试中常见的二叉树操作作个总结:前序遍历,中序遍历,后序遍历;层次遍历;求树的结点数;求树的叶子数;求树的深度;求二叉树第k层的结点个数;判断两棵二叉树是否结构相同;求二叉树的镜像;求两个结点的最低公共祖先结点;求任意两结点距离;找出二叉树中某个结点的所有祖先结点;不使用递归和栈遍历二叉树;二叉树前序中序推后序;判断二叉树是不是完全二叉树;判断是否是二叉查找树的后序遍历结果;给定一个二叉查...
Excel聚光灯效果终极指南:VBA代码实现与条件格式避坑技巧
本文详细解析了Excel聚光灯效果的实现方法,包括VBA代码优化与条件格式的避坑技巧。通过模块化编程和性能优化技术,帮助用户高效处理大型数据表格,实现自定义高亮显示,提升数据分析和报表阅读体验。
当Kmer遇到多倍体:复杂基因组Survey分析中的避坑指南
本文针对小麦、棉花等多倍体及高杂合复杂基因组的Survey分析,深入解析了Kmer分布图解读中的常见陷阱。文章重点探讨了如何超越理想二倍体模型,利用Smudgeplot等工具准确判断基因组倍性,并提供了从参数优化、污染判断到结果验证的全流程实战指南,帮助研究者规避错误,获得更准确的基因组大小、杂合度等关键信息。
数据结构之二叉树(C语言附详细代码)
关于树的简要概念和二叉树的重点内容,以及二叉树的操作代码,前中后序遍历代码以及层次遍历代码
内存管理(二)——内存分段
内存分段
Leetcode——二叉树常考算法整理
二叉树常考算法整理 希望通过写下来自己学习历程的方式帮助自己加深对知识的理解,也帮助其他人更好地学习,少走弯路。也欢迎大家来给我的Github的Leetcode算法项目点star呀~~ 二叉树常考算法整理 前言 二叉树的类型 算法分类 遍历(Traversal)问题 先序、中序与后序遍历 利用两种遍历结果构造二叉树 递归问题 二叉树最大深度 二叉树最小...
实战拆解:RAG vs Agent在金融风控中的性能对比
2024年上半年我帮某华东地区城商行做零售交易反欺诈系统升级时,技术团队曾发生过一次激烈争论:一派认为应该优先上RAG(检索增强生成)方案,成本低、落地快、合规性好;另一派坚持要上Agent(智能体)方案,推理能力强、能应对复杂新型欺诈。最终我们做了完整的AB测试,结论是两者没有绝对的优劣,只有场景适配的差异:RAG适合高并发、低时延的实时风控场景,Agent适合低并发、高准确率要求的事后核查/大额审批场景,两者结合的 hybrid 方案能覆盖95%以上的风控需求。本文我会结合真实项目的测试数据,从核心概念
ACM论文LaTeX模板解析(三)| 文章顶部信息(Top matter))Part 1
文章顶部信息(Top matter))Part 1,文章类型、作者信息等
二叉树类型笔试面试题大总结(含代码)
一、二叉树的遍历-前序、中序、后序以及层次遍历(递归与非递归) 参考另外一篇笔记《二叉树的遍历-递归与非递归 -海子 - 博客园》。 二、重建二叉树,依据前序遍历结果和中序遍历结果 《剑指Offer》面试题6. 思想:递归 代码: // 《剑指Offer——名企面试官精讲典型编程题》代码 // 著作权所有者:何海涛 struct BinaryTreeNode { i...
灵境视界:基于云计算与深度学习的智能手语识别AR眼镜
灵境视界”不仅仅是一款AR眼镜,它代表了未来无障碍沟通设备的发展方向。随着云计算与深度学习技术的不断进步,未来我们将进一步优化识别算法,提升翻译精度,并扩展更多语言和手语类型,为全球听障人士提供更加全面的沟通解决方案。是一款融合了云计算与深度学习技术的增强现实(AR)眼镜,专为听障人士和手语使用者设计。这款眼镜不仅具备先进的AR显示功能,还能实时识别并翻译手语,打破听障人士与健听人士之间的沟通障碍,推动无障碍社会的建设。,让沟通无界,视界无限。
面试中的二叉树问题总结【Java版】
面试中的二叉树问题总结【Java版】 2014-10-16 13:42 3718人阅读 评论(0) 收藏 举报 本文章已收录于: 分类: 数据结构(6) 作者同类文章X •二叉树的常见问题及其解决程序 •hash_set哈希集合容器 •list和vector有什么区别? •经典面试题:设计包含min函数的栈,O(1)
5、自动化代码结构与运维系统的优化
本文探讨了自动化代码结构与运维系统的优化方法,涵盖审批流程的模块化设计、日志记录与工单系统的集成、通知方式的选择、错误处理的策略以及持续改进的实践。同时,深入分析了运维盲目性问题,通过故障案例揭示了关键指标缺失和日志未充分利用的弊端,提出了开发与运维角色转变的必要性。文章还介绍了如何生成有用的系统指标、建立良好的日志习惯以及实施日志聚合,以提升系统的可观测性和可靠性。最终通过总结和流程图展示了优化措施的核心要点,为构建高效、可靠的运维体系提供了指导。
lwip-2.0.3
lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。
java后端面试问题,二叉树,spring常见面试题总结
斜树:所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。满二叉树:在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。完全二叉树:对一颗具有n个结点的二叉树按层编号,如果编号为i(1
怀旧版电脑GBC模拟器
再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。
Java开发面试问题,java项目常见问题
20个二叉树面试高频 0. 几个概念 1. 求二叉树中的节点个数 2. 求二叉树的最大层数(最大深度) 3. 先序遍历/前序遍历 4. 中序遍历 5. 后序遍历 6. 分层遍历 7. 求二叉树第K层的节点个数 8. 求二叉树第K层的叶子节点个数 9. 判断两棵二叉树是否结构相同 10. 判断二叉树是不是平衡二叉树 11. 求二叉树的镜像 12. 求二叉树中两个节点的最低公共祖先节点 13. 求二叉树的直径 14. 由前序遍历序列和中序遍历序列重建二叉树 15. 判断二叉树是不是完全二叉树 16. 树的子结
OV9734 DATASHEET,完整规格书
OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等
IDEA的使用指南
IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.
java二叉树动态_一文搞定Java面试中的数据结构问题
Java面试过程中,经常会被问到数据结构和算法相关的知识。对于工作多年的程序员来说,这些理论的知识可能已经忘得差不多了吧,所以面试前还是有必要临时抱抱佛脚的。其实面试官的要求也很简单,只要大体懂得其原理即可,并不太会深究,尤其对社招。校招在此不做讨论,毕竟刚出校园,理论知识正值巅峰,哈哈。一、线性表(重点)线性表是由N个元素组成的有序序列,也是最常见的一种数据结构。重点有两个数组和链表。1、数组数...
java二叉树求权值_二叉树求最大最小权值问题的面试题
有一棵二叉树,树上每个点标有权值,权值各不相同,给定二叉树的根节点为root,请找出权值最大的节点与权值最小的节点; 假设父子节点距离为1,请计算权值最大节点与权值最小节点之间的距离(提示:可分别找出根节点到该两点间的路径,再减去两者从根节点开始的共同路径距离)在很多面试中也有这样类似的问题,自己本身基础很弱,将学过的思路整理出来,方便自己以后的学习。话不多说,上思路。思路:先求最大最小叶子节点,...
Java企业面试算法新得体会之3二叉树问题24问
二叉树问题24问
Java数据结构——7.3二叉树-OJ面试题
比特就业课Java数据结构——二叉树-OJ面试题 课后思维总结
最全【万字好文】二叉树从入门到精通(树、二叉树、堆,网商银行java面试
包含最全MySQL、Redis、Java并发编程等等面试题和答案,用于参考~
最全【万字好文】二叉树从入门到精通(树、二叉树、堆(1),springboot启动方式面试题
虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料。
每日一道算法:20200622-二叉树翻转
问题描述 翻转一棵二叉树。 示例: 输入: 4 / 2 7 / \ / 1 3 6 9 输出: 4 / 7 2 / \ / 9 6 3 1 备注: 这个问题是受到 Max Howell 的 原问题 启发的 : 谷歌:我们90%的工程师使用您编写的软件(Homebrew),但是您却无法在面试时在白板上写出翻转二叉树这道题,这太糟糕了。 Related Topics 树 解决方案 这是一个非常经典的树的问题,这个问题很适合用递归方法来解决
3299



被折叠的 条评论
为什么被折叠?



