目录
二叉树的“双指针”问题
下面我们来学习几个有关二叉树的算法问题,其中的双指针就是定义了两个变量,这两个指针可能针对一棵树,也可能针对两棵树,所以我们也姑且称之为“双指针”吧,好理解就行。这些问题一般与判断相同、对称、反转和合并等类型相关,接下来就让我们一起学习几个经典的问题吧。
一 判断两树是否相同
给定两棵二叉树的节点p和q,如果两棵树在结构上相同,并且相同位置的节点具有相同的值,则认为它们是相同的。如下所例:
输入:p = [1,2,3] q = [1,2,3]
输出: true
其实就是同时对两个二叉树进行前序遍历,先判断两棵树的根节点是否相同,如果相同再分别判断左右子节点是否相同,判断过程中只要有一个不相同就要返回false,说明两棵树不相同,只有全部相同最后才会返回true,两棵树才相同。实现代码如下:
public boolean isSameTree(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 isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
二 对称二叉树
给定一个二叉树,判断它是否镜像对称。如下所例:
输入: 1
/ \
2 2
/ \ / \
3 4 4 3
输出: true
因此我们要判断根节点左右两个子树的内侧节点和外侧节点是否都相等,在此可以通过递归函数的返回值来判断。所以准确来说就是一个树采用左右中的遍历顺序,另一个树采用右左中的遍历顺序。其中单层递归的逻辑就是处理左右节点都不为空,且数值相同的情况。具体步骤如下:
1) 比较当前节点左右两子树外侧是否对称:传入左节点的左孩子,右节点的右孩子。
2) 比较当前节点左右两子树内测是否对称:传入左节点的右孩子,右节点的左孩子。
3) 如果有一侧不对称就返回false,如果左右都对称就返回true。
具体实现代码如下:
class Solution {
public boolean isSymmetric(TreeNode root) {
// 如果root为空,则认为其对称
if (root == null) return true;
// 同时检查左右节点, 后面通过递归遍历对应节点是否相同
return isSame(root.left, root.right);
}
// 检查对应节点是否相同的函数
public boolean isSame(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 isSame(p.left, q.right) && isSame(p.right, q.left);
}
}
三 合并二叉树
给定两个二叉树,将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。如下所例:
输入:
Tree 1: 1 Tree 1: 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
输入:
Tree 1: 3
/ \
4 5
/ \ \
5 4 7
合并两个二叉树的对应节点可能存在如下三种情况,对于每种情况要使用不同的合并方式。
1) 如果两个二叉树的对应节点都为空,则合并后的二叉树该对应节点也为空;
2) 如果两个二叉树的对应节点只有一个为空,则合并后的二叉树该对应节点为其中的非空节点;
3) 如果两个二叉树的对应节点都不为空,则合并后的二叉树该对应节点的值为两个二叉树对应节点的值之和。
在对一个节点进行合并之后,还要通过递归的方式继续合并该节点的左右孩子。
具体实现代码如下:
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
// 如果第一个节点为空, 则第二个节点为新的节点(可能为空)
if (root1 == null) return root2;
// 如果第二个节点为空, 则第一个节点为新的节点(可能为空)
if (root2 == null) return root1;
// 如果两个节点都不为空,那么两个节点的值相加后作为合并节点的新值
TreeNode mergedNode = new TreeNode(root1.val + root2.val);
// 合并完该节点后,继续递归合并其左右节点
mergedNode.left = mergeTrees(root1.left, root2.left);
mergedNode.right = mergeTrees(root1.right, root2.right);
return mergedNode;
}
四 判断两树是否对称
在前面的基础上,现在给定两棵二叉树,我们要来判断这两棵二叉树是否镜像对称。其实实现方法很简单,只需要在第一种题型(判断两棵树是否相同)做一点小小的改变即可。
判断两树是否相同是判断相同位置的节点是否相同,所以若要判断两树是否对称,只需判断左树的外侧节点和右树的外侧节点是否相同,以及左树的内侧节点和右树的外侧节点是否相同。所以只需对第一题的代码做如下一点小小的改变即可:
public static boolean isSymmetric(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 isSymmetric(p.left, q.right) && isSymmetric(p.right, q.left);
}