1. 二叉树中的双指针
1.1 判断两棵树是否相同
LeetCode100:给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
输入:p = [1,2,3], q = [1,2,3]
输出:true
输入:p = [1,2], q = [1,null,2]
输出:false
思路: 两个二叉树同时进行前序遍历, 先判断根节点是否相同, 如果相同再分别判断左右子节点是否相同, 判断的过程中只要有一个不相同就返回 false, 如果全部相同才返回 true。
代码实现:
public static boolean isSameTree(TreeNode p, TreeNode q) {
//如果都为空我们就认为他是相同的
if (p == null && q == null)
return true;
//如果一个为空,一个不为空,很明显不可能是相同的树,直接返回false即可
if (p == null || q == null)
return false;
//如果这两个节点都不为空并且又不相等,所以他也不可能是相同的树,直接返回false
if (p.val != q.val)
return false;
//走到这一步说明节点p和q是完全相同的,我们只需要在比较他们的左右子节点即可
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
1.2 对称二叉树
LeetCode101 给定一个二叉树,检查它是否是镜像对称的。例如下面这个就是对称二叉树:
思路: 通过递归函数判断两个子树的内侧节点和外侧节点是否相等, 一个树的遍历顺序为左右中, 一个树的遍历顺序为右左中。
- 比较二叉树外侧是否对称: 传入左节点的左孩子和右节点的右孩子
- 比较内测是否对称: 传入左节点的右孩子和左节点的左孩子
- 如果左右都对称就返回 true, 有一侧不对称就返回 false
代码实现:
public static boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
return check(root.left, root.right);
}
public static boolean check(TreeNode p, TreeNode q) {
if (p == null && q == null) {
return true;
}
if (p == null || q == null) {
return false;
}
if (p.val != q.val) {
return false;
}
return check(p.left, q.right) && check(p.right, q.left);
}
1.3 合并二叉树
LeetCode617.给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。示例:
思路: 两个二叉树的对应节点可能存在以下三种情况, 对于每种情况使用不同的合并方式:
- 如果两个二叉树的节点都为空, 则合并后的二叉树的对应节点也为空;
- 如果两个二叉树的对应节点只有一个为空, 则合并后的二叉树的对应节点为其中的非空节点;
- 如果两个二叉树的对应节点都不为空, 则合并后的二叉树的对应节点的值为两个二叉树的对应节点的值之和, 此时需要显性合并两个节点。
对一个节点合并以后, 还要对该节点的左右子树分别进行合并。
代码实现:
public static 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. 路径专题
2.1 二叉树的所有路径
LeetCode257:给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点是指没有子节点的节点。示例:
输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]
思路: 有几个叶子节点, 就有几条路径。从根节点开始一直向下找, 每找到一个子节点, 就判断该节点是否为叶子节点, 如果是叶子节点, 则增加一条路径。在访问每个节点时, 将该节点存入集合中, 到叶子节点的时候再添加到集合里, 形成完整的路径。
private static void dfs(TreeNode root, String path, List<String> res) {
//如果为空,直接返回
if (root == null)
return;
//如果是叶子节点,说明找到了一条路径,把它加入到res中
if (root.left == null && root.right == null) {
res.add(path + root.val);
return;
}
//如果不是叶子节点,在分别遍历他的左右子节点
dfs(root.left, path + root.val + "->", res);
dfs(root.right, path + root.val + "->", res);
}
2.2 路径总和
LeetCode112题:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum。叶子节点 是指没有子节点的节点。
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
思路: 假定从根节点到当前节点的值之和为 val, 可以将整个大问题转化为一个小问题: 是否存在从当前节点的子节点到叶子节点的路径, 满足其路径之和为 sum - val。若当前节点就是叶子节点, 那么直接判断 sum 是否等于 val 即可, 若当前节点不是叶子节点, 只需要递归询问它的子节点是否满足条件。
代码实现:
public static boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
if (root.left == null && root.right == null) {
return sum == root.val;
}
boolean left = hasPathSum(root.left, sum - root.val);
boolean right = hasPathSum(root.right, sum - root.val);
return left || right;
}
3. 翻转
LeetCode226 翻转二叉树,将二叉树整体反转。如下图所示:
思路: 翻转树, 就是将每一个节点的左右孩子做交换。我们从根节点开始, 递归地对数进行遍历, 并从叶子节点先开始翻转。如果当前遍历到节点 root, 且左右子树都已经翻转, 那么我们只需要交换两棵子树的位置。
代码实现:
public static TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree_1(root.left);
invertTree_1(root.right);
return root;
}