二叉树进阶:经典算法题详解
二叉树进阶:经典算法题详解
- 一、双指针相关问题
- 1.1 判断两棵树是否相同
- 题目描述
- 解题思路
- Java代码实现
- 1.2 对称二叉树
- 题目描述
- 解题思路
- Java代码实现
- 1.3 合并二叉树
- 题目描述
- 解题思路
- Java代码实现
- 二、路径相关问题
- 2.1 二叉树的所有路径
- 题目描述
- 解题思路
- Java代码实现
- 2.2 路径总和
- 题目描述
- 解题思路
- Java代码实现
- 三、经典算法题补充
- 3.1 翻转二叉树
- 题目描述
- 解题思路
- Java代码实现(递归)
- Java代码实现(迭代)
- 3.2 从前序与中序遍历序列构造二叉树
- 题目描述
- 解题思路
- Java代码实现
二叉树作为数据结构中的重要组成部分,其相关算法题在算法竞赛、技术面试以及实际开发中频繁出现,掌握二叉树经典算法题的解题思路与技巧,是进阶算法领域的关键一步。本文我将通过详细解析挑选的几道经典二叉树算法题,结合Java代码实现,帮你深入理解二叉树算法的精髓。
一、双指针相关问题
1.1 判断两棵树是否相同
题目描述
给定两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。两棵树被认为是相同的,如果它们的结构相同,并且节点具有相同的值。(LeetCode 100)
解题思路
采用递归的方式同时对两棵树进行前序遍历。从根节点开始,首先判断两棵树的根节点是否都为空,若都为空,则两棵树相同;若其中一个为空,另一个不为空,则两棵树不同。若根节点都不为空且值相等,则继续递归判断左子树和右子树是否相同。
Java代码实现
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class SameTree {public boolean isSameTree(TreeNode p, TreeNode q) {// 若两棵树的根节点都为空,返回trueif (p == null && q == null) {return true;}// 若其中一个根节点为空,另一个不为空,返回falseif (p == null || q == null) {return false;}// 若根节点值不相等,返回falseif (p.val != q.val) {return false;}// 递归判断左子树和右子树是否相同return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);}
}
1.2 对称二叉树
题目描述
给定一个二叉树的根节点 root
,检查它是否轴对称。(LeetCode 101)
解题思路
通过递归函数判断二叉树的内侧(左子树的右节点和右子树的左节点)和外侧(左子树的左节点和右子树的右节点)是否对称。首先判断根节点是否为空,若为空则是对称的。然后调用辅助递归函数,判断根节点的左子树和右子树是否对称。在辅助递归函数中,同样先判断两个节点是否都为空,若都为空则对称;若其中一个为空,另一个不为空则不对称;若都不为空且值相等,则继续递归判断内侧和外侧是否对称。
Java代码实现
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class SymmetricTree {public boolean isSymmetric(TreeNode root) {if (root == null) {return true;}return isMirror(root.left, root.right);}private boolean isMirror(TreeNode left, TreeNode right) {if (left == null && right == null) {return true;}if (left == null || right == null) {return false;}return (left.val == right.val)&& isMirror(left.left, right.right)&& isMirror(left.right, right.left);}
}
1.3 合并二叉树
题目描述
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。你需要将它们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将它们的值相加作为节点合并后的新值,否则不为 null
的节点将直接作为新二叉树的节点。(LeetCode 617)
解题思路
采用递归的方式合并两棵树。从根节点开始,若其中一棵树为空,则返回另一棵树;若两棵树都不为空,则创建一个新节点,其值为两棵树对应节点值之和,然后递归合并左子树和右子树。
Java代码实现
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class MergeTrees {public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {if (t1 == null) {return t2;}if (t2 == null) {return t1;}TreeNode merged = new TreeNode(t1.val + t2.val);merged.left = mergeTrees(t1.left, t2.left);merged.right = mergeTrees(t1.right, t2.right);return merged;}
}
二、路径相关问题
2.1 二叉树的所有路径
题目描述
给定一个二叉树,返回所有从根节点到叶子节点的路径。(LeetCode 257)
解题思路
使用深度优先搜索(DFS)的方法,从根节点开始递归遍历二叉树。在递归过程中,用一个字符串记录从根节点到当前节点的路径。当到达叶子节点时,将该路径加入结果列表中。对于每个节点,递归处理其左子树和右子树,并在递归返回时回溯,恢复路径字符串的状态。
Java代码实现
import java.util.ArrayList;
import java.util.List;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class BinaryTreePaths {public List<String> binaryTreePaths(TreeNode root) {List<String> paths = new ArrayList<>();if (root == null) {return paths;}dfs(root, "", paths);return paths;}private void dfs(TreeNode node, String path, List<String> paths) {path += node.val;if (node.left == null && node.right == null) {paths.add(path);return;}if (node.left != null) {dfs(node.left, path + "->", paths);}if (node.right != null) {dfs(node.right, path + "->", paths);}}
}
2.2 路径总和
题目描述
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
,判断该树中是否存在 从根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。叶子节点 是指没有子节点的节点。(LeetCode 112)
解题思路
将问题拆解为子问题,使用递归的方式从根节点开始判断。首先判断根节点是否为空,若为空则直接返回 false
。然后判断根节点是否为叶子节点且其值等于目标和,若是则返回 true
。否则,递归判断左子树和右子树是否存在满足条件的路径,即将目标和减去当前节点的值后,继续在子树中寻找。
Java代码实现
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class PathSum {public boolean hasPathSum(TreeNode root, int targetSum) {if (root == null) {return false;}if (root.left == null && root.right == null && root.val == targetSum) {return true;}return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val);}
}
三、经典算法题补充
3.1 翻转二叉树
题目描述
给你一棵二叉树的根节点 root
,翻转这棵二叉树,并返回其根节点。(LeetCode 226)
解题思路
可以使用递归或迭代的方式实现。递归方式下,从根节点开始,交换其左右子树,然后递归地翻转左子树和右子树。迭代方式可以使用栈来模拟递归过程,先将根节点入栈,然后在栈不为空时,弹出节点,交换其左右子树,并将非空的左右子节点入栈,直到栈为空。
Java代码实现(递归)
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class InvertBinaryTree {public TreeNode invertTree(TreeNode root) {if (root == null) {return null;}TreeNode left = invertTree(root.left);TreeNode right = invertTree(root.right);root.left = right;root.right = left;return root;}
}
Java代码实现(迭代)
import java.util.Stack;class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class InvertBinaryTreeIterative {public TreeNode invertTree(TreeNode root) {if (root == null) {return null;}Stack<TreeNode> stack = new Stack<>();stack.push(root);while (!stack.isEmpty()) {TreeNode node = stack.pop();TreeNode temp = node.left;node.left = node.right;node.right = temp;if (node.left != null) {stack.push(node.left);}if (node.right != null) {stack.push(node.right);}}return root;}
}
3.2 从前序与中序遍历序列构造二叉树
题目描述
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的前序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。(LeetCode 105)
解题思路
前序遍历的第一个元素是根节点的值,在中序遍历中找到该值的位置,这个位置将中序遍历序列分为左子树和右子树两部分。根据左子树和右子树的节点数量,可以在前序遍历序列中划分出左子树和右子树的前序遍历部分。然后递归地构造左子树和右子树,最终得到整棵二叉树。
Java代码实现
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) { val = x; }
}public class BuildTree {public TreeNode buildTree(int[] preorder, int[] inorder) {return buildTree(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);}private TreeNode buildTree(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {if (preStart > preEnd || inStart > inEnd) {return null;}int rootVal = preorder[preStart];TreeNode root = new TreeNode(rootVal);int inIndex = 0;for (int i = inStart; i <= inEnd; i++) {if (inorder[i] == rootVal) {inIndex = i;break;}}int leftSize = inIndex - inStart;root.left = buildTree(preorder, preStart + 1, preStart + leftSize, inorder, inStart, inIndex - 1);root.right = buildTree(preorder, preStart + leftSize + 1, preEnd, inorder, inIndex + 1, inEnd);return root;}
}
That’s all, thanks for reading!
觉得有用就点个赞
、收进收藏
夹吧!关注
我,获取更多干货~