二叉树问题讨论(部分内容待补充)
一,判断两颗树是否相同
(一)核心案例
1,相同树案例
两颗树结构完全一致,并且对应节点值完全相同。说人话就是长得一模一样。
2,结构不同案例
树 1“根节点 1,左子节点 2”,树 2“根节点 1,右子节点 2”,结构不同,非相同树
3,值不同案例
就是结构一样但是二叉树所存储的值不同
public boolean isSameTree(TreeNode p,TreeNode q){//情况1,一个为空另一个不为空if(p==null&&q!=null)||(p!=null&&q==null){return false;
}//情况二,两个都为空 if(p==null&&q==null){return true;
} //结构相同判断值是否相同if(p.val!=q.val){return false;}
//利用递归的方法return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);} 二,判断一颗树是否为另一颗树的子树
核心案例二
1,递归思想:将判断整棵树是否相同拆解为判断根节点是否相同拆解为
a.判断根节点是否相同b.左子树是否相同c.右子树是否相同。
通过递归逐层验证。
2,结构优先判断:先通过节点是否为空判断结构是否一致,在判断节点值是否相同,避免无效的数值比较。
3,边界处理:空树是特殊情况,两个空树视为相同,一个空树一个非空视为不同,覆盖所有的边界场景
public blooean isSameTree(TreeNode p,TreeNode q){if((p==null&&q=!=null)||(q==null&&p=!=null)){return false;}if(p==null&&q==null){return false;
} if(p.val!=q.vall){return false;
} return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);}public boolean isSubtree(TreeeNode root ,TreeNode subRoot){if (root==null){return false;
} if( isSameTree(root ,subRoot)){return true;}return isSubtree(root.left,subRoot)&&isSubtree(root.right,subRoot);}三,反转二叉树
核心案例
1,基础反转案例,原树为 “根节点 4,左子节点 2(左子节点 1,右子节点 3),右子节点 7(左子节点 6,右子节点 9)”,反转后为 “根节点 4,左子节点 7(左子节点 9,右子节点 6),右子节点 2(左子节点 3,右子节点 1)”,每个节点的左右子树完全交换。
2,空树、单位节点树案例,空树反转后仍为空树;单节点树(仅根节点 5)反转后仍为自身,无左右子树可交换。
参考代码
public TreeNode invertTree(TreeNode root){if (root ==null){return null;}if (root.left == null && root.right == null) {return root;}TreeNode temp=root.left;root.left=root.right;root.right=temp;invertTree(root.left);// 反转右子树invertTree(root.right);// 反转左子树return root;}四,判断二叉树是否对称‘
核心案例
1,对称数案例:树为 “根节点 1,左子节点 2(左子节点 3,右子节点 4),右子节点 2(左子节点 4,右子节点 3)”,以根节点为中线,左子树的左节点与右子树的右节点相同(3 和 3),左子树的右节点与右子树的左节点相同(4 和 4),整体对称。
2,非对称树案例(结构不对称):树为 “根节点 1,左子节点 2(右子节点 3),右子节点 2(右子节点 3)”,左子树的左为空,右子树的右为空,但左子树的右与右子树的右结构不匹配,非对称。
3,非对称树案例(值不对称)树为 “根节点 1,左子节点 2(左子节点 3),右子节点 2(左子节点 4)”,结构对称但对应节点值(3 和 4)不同,非对称。
public boolean isMirror(TreeNode leftTree,TreeNode rightTree){if((leftTree==null&&rightTree!=null)||(rightTree==null&&leftTree!=null)){return false;
}if (leftTree==null&&rightTtree==null){return true;}if(leftTee.val!=rightTree.val){return false;
} return isMirror( leftTree.left, rightTree.right)&&isMirror(leftTree.right, rightTree.left);
}
public boolean isSymmetric(Tree root){if(root ==null){return true;}return isMirror(root.left, root.right);}五,判断二叉树是否平衡
核心案例
1,平衡树案例
树为 “根节点 3,左子节点 9,右子节点 20(左子节点 15,右子节点 7)”,每个节点的左右子树高度差均≤1(根节点左高 1、右高 2,差 1;20 左高 1、右高 1,差 0;9 无后代,差 0),为平衡树。
2,非平衡树案例
树为 “根节点 1,左子节点 2(左子节点 3,左子节点 4),右子节点 2”,根节点左子树高度 3、右子树高度 1,差 2>1,非平衡树。
public int getHeight (TreeNode root){if(root==null){return 0;
}return 1+Math.max(getHeight(root.left),getHeight(root.right));
}public blooean isBalanced(TreeNode root){if(root==null){return true;
}int leftHeight = getHeight(root.left);int rightHeight = getHeight(root.right);return Math.abs(leftHeight -rightHeight)<=1&& isBalanced(root.left)&& isBalanced(root.right);}优化后的代码
// 辅助方法:求高度的同时判断是否平衡,不平衡返回-1
private int getHeightAndCheckBalance(TreeNode root) {if (root == null) {return 0;}// 求左子树高度,若左子树不平衡,直接返回-1int leftHeight = getHeightAndCheckBalance(root.left);if (leftHeight == -1) {return -1;}// 求右子树高度,若右子树不平衡,直接返回-1int rightHeight = getHeightAndCheckBalance(root.right);if (rightHeight == -1) {return -1;}// 若当前节点不平衡,返回-1;否则返回当前节点的高度if (Math.abs(leftHeight - rightHeight) > 1) {return -1;}return 1 + Math.max(leftHeight, rightHeight);
}public boolean isBalanced(TreeNode root) {// 若返回值≠-1,说明树平衡return getHeightAndCheckBalance(root) != -1;
}六 ,二叉树的创建
import java.util.Scanner;public class CreatTree {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String str = scanner.nextLine();int[] index = {0}; // 用数组存储索引,解决递归中索引共享问题TreeNode root = creatNode(str, index); // 传入索引参数inorderNode(root); // 修正方法名拼写(inoredrNode→inorderNode)}scanner.close(); // 补充关闭资源}public static TreeNode creatNode(String str, int[] index) {TreeNode root = null;// 避免索引越界if (index[0] >= str.length()) {return null;}// 读取当前索引位置的字符char c = str.charAt(index[0]);if (c != '#') { // 非空节点root = new TreeNode(c);index[0]++;root.left = creatNode(str, index);root.right = creatNode(str, index);} else {index[0]++;}return root;}public static void inorderNode(TreeNode root) {if (root == null) {return;}inorderNode(root.left);System.out.print(root.val + " ");inorderNode(root.right);}
}
public class TreeNode {Character val;TreeNode left;TreeNode right;public TreeNode( char val) {this.val = val;left=null;right=null;}
}
基于栈的实现方案(逆序入站哦)
import java.util.Scanner;
import java.util.Stack;public class CreatTree {static class TreeNode {char val;TreeNode left;TreeNode right;TreeNode(char val) {this.val = val;left = null;right = null;}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String str = scanner.nextLine();// 将字符串转换为栈(按顺序入栈,栈顶为第一个字符)Stack<Character> stack = new Stack<>();for (int i = str.length() - 1; i >= 0; i--) { // 逆序入栈,保证弹出顺序与字符串一致stack.push(str.charAt(i));}TreeNode root = creatNode(stack); // 传入栈构建树inorderNode(root);System.out.println(); // 换行区分多次输入}scanner.close();}// 用栈构建二叉树,每次弹出栈顶元素处理public static TreeNode creatNode(Stack<Character> stack) {// 栈为空,返回空节点if (stack.isEmpty()) {return null;}// 弹出栈顶元素(当前待处理字符)char c = stack.pop();if (c == '#') { // '#'表示空节点return null;}// 非空节点:创建节点,递归构建左右子树(栈已弹出当前字符,后续处理下一个)TreeNode root = new TreeNode(c);root.left = creatNode(stack); // 先构建左子树(栈顶已更新为下一个字符)root.right = creatNode(stack); // 再构建右子树return root;}// 中序遍历(递归,也可改为栈实现的非递归遍历)public static void inorderNode(TreeNode root) {if (root == null) {return;}inorderNode(root.left);System.out.print(root.val + " ");inorderNode(root.right);}
}七,二叉树的分层遍历
import java.util.*;// 二叉树节点定义
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;left = null;right = null;}
}public class BinaryTreeLevelOrderTraversal {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> result = new ArrayList<>();if (root == null) {return result;}// 使用队列实现层次遍历Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {// 当前层的节点数量int levelSize = queue.size();List<Integer> currentLevel = new ArrayList<>();// 遍历当前层的所有节点for (int i = 0; i < levelSize; i++) {TreeNode node = queue.poll();currentLevel.add(node.val);// 将下一层的节点加入队列if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}// 将当前层的结果加入总结果result.add(currentLevel);}return result;}// 测试代码public static void main(String[] args) {// 构建示例二叉树TreeNode root = new TreeNode(3);root.left = new TreeNode(9);root.right = new TreeNode(20);root.right.left = new TreeNode(15);root.right.right = new TreeNode(7);BinaryTreeLevelOrderTraversal solution = new BinaryTreeLevelOrderTraversal();List<List<Integer>> result = solution.levelOrder(root);// 打印结果for (List<Integer> level : result) {System.out.println(level);}}
}八,给定一个二叉树,找到该树中两个指定节点的最近公共祖先。
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class LowestCommonAncestor {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {// 边界条件:若root为null,或root是p/q,直接返回rootif (root == null || root == p || root == q) {return root;}// 递归查找左子树TreeNode left = lowestCommonAncestor(root.left, p, q);// 递归查找右子树TreeNode right = lowestCommonAncestor(root.right, p, q);// 左右子树分别找到p和q,当前节点是LCAif (left != null && right != null) {return root;}// 只在左子树找到,返回左子树结果// 只在右子树找到,返回右子树结果return left != null ? left : right;}
}
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Stack;public class LowestCommonAncestor {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {// 存储节点与其父节点的映射Map<TreeNode, TreeNode> parentMap = new HashMap<>();// 根节点没有父节点parentMap.put(root, null);// 用栈遍历树,记录所有节点的父节点Stack<TreeNode> stack = new Stack<>();stack.push(root);while (!stack.isEmpty()) {TreeNode node = stack.pop();// 记录左子树的父节点if (node.left != null) {parentMap.put(node.left, node);stack.push(node.left);}// 记录右子树的父节点if (node.right != null) {parentMap.put(node.right, node);stack.push(node.right);}}// 记录p的所有祖先(包括p自身)Set<TreeNode> pAncestors = new HashSet<>();while (p != null) {pAncestors.add(p);p = parentMap.get(p); // 向上移动到父节点}// 从q向上追溯,第一个出现在p的祖先集合中的节点即为LCAwhile (q != null) {if (pAncestors.contains(q)) {return q;}q = parentMap.get(q); // 向上移动到父节点}return null; // 理论上不会走到这里(题目保证p和q在树中)}
}九,根据一棵树的前序遍历与中序遍历构造二叉树。
import java.util.HashMap;
import java.util.Map;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class BuildTree {// 存储中序遍历中值与索引的映射,加速查找private Map<Integer, Integer> inorderMap;// 前序遍历数组private int[] preorder;public TreeNode buildTree(int[] preorder, int[] inorder) {this.preorder = preorder;inorderMap = new HashMap<>();// 构建中序遍历的映射表for (int i = 0; i < inorder.length; i++) {inorderMap.put(inorder[i], i);}// 递归构造二叉树return build(0, preorder.length - 1, 0, inorder.length - 1);}/*** 递归构建二叉树* @param preStart 前序遍历起始索引* @param preEnd 前序遍历结束索引* @param inStart 中序遍历起始索引* @param inEnd 中序遍历结束索引* @return 构建的子树的根节点*/private TreeNode build(int preStart, int preEnd, int inStart, int inEnd) {// 终止条件:索引越界if (preStart > preEnd || inStart > inEnd) {return null;}// 前序遍历的第一个元素是当前根节点int rootVal = preorder[preStart];TreeNode root = new TreeNode(rootVal);// 找到根节点在中序遍历中的位置int rootIndexInOrder = inorderMap.get(rootVal);// 左子树的节点数量int leftSize = rootIndexInOrder - inStart;// 递归构建左子树root.left = build(preStart + 1, // 左子树前序起始索引preStart + leftSize, // 左子树前序结束索引inStart, // 左子树中序起始索引rootIndexInOrder - 1 // 左子树中序结束索引);// 递归构建右子树root.right = build(preStart + leftSize + 1, // 右子树前序起始索引preEnd, // 右子树前序结束索引rootIndexInOrder + 1, // 右子树中序起始索引inEnd // 右子树中序结束索引);return root;}// 测试代码public static void main(String[] args) {int[] preorder = {3, 9, 20, 15, 7};int[] inorder = {9, 3, 15, 20, 7};BuildTree solution = new BuildTree();TreeNode root = solution.buildTree(preorder, inorder);// 可通过前序/中序遍历验证结果}
}十,二叉树创建字符串。
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class Tree2Str {public String tree2str(TreeNode root) {// 空树直接返回空字符串if (root == null) {return "";}// 叶子节点直接返回其值if (root.left == null && root.right == null) {return String.valueOf(root.val);}// 只有右子树时,左子树需要用空括号表示if (root.left == null) {return root.val + "()(" + tree2str(root.right) + ")";}// 只有左子树时,右子树可以省略括号if (root.right == null) {return root.val + "(" + tree2str(root.left) + ")";}// 左右子树都存在时,都需要用括号包裹return root.val + "(" + tree2str(root.left) + ")" + "(" + tree2str(root.right) + ")";}// 测试代码public static void main(String[] args) {// 构建示例二叉树TreeNode root = new TreeNode(1);root.left = new TreeNode(2);root.right = new TreeNode(3);root.left.left = new TreeNode(4);Tree2Str solution = new Tree2Str();System.out.println(solution.tree2str(root)); // 输出: 1(2(4))(3)// 另一测试用例(左子树为空)TreeNode root2 = new TreeNode(1);root2.left = null;root2.right = new TreeNode(2);root2.right.left = new TreeNode(3);System.out.println(solution.tree2str(root2)); // 输出: 1()(2(3))}
}十一,二叉树前序非递归遍历实现。
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class PreorderTraversal {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null) {return result;}// 用栈存储节点Stack<TreeNode> stack = new Stack<>();// 根节点先入栈stack.push(root);// 栈不为空时循环while (!stack.isEmpty()) {// 弹出栈顶节点(当前节点)并访问TreeNode node = stack.pop();result.add(node.val);// 右子节点先入栈(后访问)if (node.right != null) {stack.push(node.right);}// 左子节点后入栈(先访问)if (node.left != null) {stack.push(node.left);}}return result;}// 测试代码public static void main(String[] args) {// 构建示例二叉树TreeNode root = new TreeNode(1);root.right = new TreeNode(2);root.right.left = new TreeNode(3);PreorderTraversal solution = new PreorderTraversal();List<Integer> result = solution.preorderTraversal(root);// 输出结果:[1, 2, 3]System.out.println(result);}
}
十二,二叉树中序非递归遍历实现。
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class InorderTraversal {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null) {return result;}Stack<TreeNode> stack = new Stack<>();TreeNode current = root; // 当前遍历的节点// 循环条件:栈不为空 或 当前节点不为空while (!stack.isEmpty() || current != null) {// 1. 将当前节点的所有左子节点入栈while (current != null) {stack.push(current);current = current.left; // 移动到左子节点}// 2. 弹出栈顶节点(左子树已遍历完),访问该节点current = stack.pop();result.add(current.val);// 3. 转向右子节点,重复上述过程current = current.right;}return result;}// 测试代码public static void main(String[] args) {// 构建示例二叉树TreeNode root = new TreeNode(1);root.right = new TreeNode(2);root.right.left = new TreeNode(3);InorderTraversal solution = new InorderTraversal();List<Integer> result = solution.inorderTraversal(root);// 输出结果:[1, 3, 2]System.out.println(result);}
}
十三,二叉树后序非递归遍历实现。
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}
}public class PostorderTraversal {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null) {return result;}Stack<TreeNode> stack = new Stack<>();TreeNode current = root;TreeNode lastVisited = null; // 记录上一次访问的节点while (!stack.isEmpty() || current != null) {// 1. 将当前节点的所有左子节点入栈while (current != null) {stack.push(current);current = current.left;}// 查看栈顶节点(不弹出)TreeNode peekNode = stack.peek();// 2. 若右子树为空 或 右子树已访问过,则访问当前节点if (peekNode.right == null || peekNode.right == lastVisited) {stack.pop(); // 弹出节点result.add(peekNode.val); // 访问节点lastVisited = peekNode; // 更新上一次访问的节点current = null; // 当前节点已处理,无需继续向左} else {// 3. 右子树未访问过,转向右子树current = peekNode.right;}}return result;}// 测试代码public static void main(String[] args) {// 构建示例二叉树TreeNode root = new TreeNode(1);root.right = new TreeNode(2);root.right.left = new TreeNode(3);PostorderTraversal solution = new PostorderTraversal();List<Integer> result = solution.postorderTraversal(root);// 输出结果:[3, 2, 1]System.out.println(result);}
}