编程导航算法通关村第八关 | 二叉树经典算法

89 阅读3分钟

二叉树中的双指针

所谓的双指针就是定义了两个变量,在二叉树中有时候也需要至少定义两个变量才能解决问题,这两个指针可能针对一棵树,也可能针对两棵树,我们姑且也称之为“双指针”吧。这些问题一般是与对称、反转和合并等类型相关

判断两棵树是否相同

. - 力扣(LeetCode)

两个二叉树同时进行前序遍历,根节点相同的话,再分别去判断左右子节点是否相同,判断过程中只要有一个不相同就返回 false

代码:

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);
}

对称二叉树

. - 力扣(LeetCode)

思路为遍历左右两颗子树,一个遍历顺序是左右中,一个遍历顺序是右左中,,单层递归的逻辑是处理左右节点不为空,且数值相同的情况

代码为:

public boolean isSymmetric(TreeNode root) {
    if(root == null) {
        return true;
    }
    return check(root.left, root.right);
}
public 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);
}

合并二叉树

两个二叉树的对应节点可能存在以下三种情况,对于每种情况使用不同的合并方式。

  • 如果两个二叉树的对应节点都为空,则合并后的二叉树的对应节点也为空;
  • 如果两个二叉树的对应节点只有一个为空,则合并后的二叉树的对应节点为其中的非空节点;
  • 如果两个二叉树的对应节点都不为空,则合并后的二叉树的对应节点的值为两个二叉树的对应节点的值之和,此时需要显性合并两个节点。

对一个节点进行合并之后,还要对该节点的左右子树分别进行合并

代码:

public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
    if(root1 == null) {
        return root2;
    }
    if(root2 == null) {
        return root1;
    }
    TreeNode merge = new TreeNode(root1.val + root2.val);
    merge.left = mergeTrees(root1.left, root2.left);
    merge.right = mergeTrees(root1.right, root2.right);
    return merge;
}

路径专题

二叉树的所有路径

. - 力扣(LeetCode)

当得到一个叶子结点容易,那这时候怎么知道它所在的完整路径是什么呢?例如上图中得到D之后,怎么知道其前面的A和B呢?简单,增加一个String类型的变量中,访问每个节点访问的时候先存到String中,到叶子节点的时候再添加到集合里 :

public List<String> binaryTreePaths(TreeNode root) {
    List<String> res = new ArrayList<>();
    dfs(root, "", res);
    return res;
}
public void dfs(TreeNode root, String path, List<String> res) {
    if(root == null) {
        return;
    }
    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);
}

路径总和

. - 力扣(LeetCode)

public boolean hasPathSum(TreeNode root, int targetSum) {
    if(root == null) {
        return false;
    }
    if(root.left == null && root.right == null) {
        return targetSum == root.val;
    }
    boolean left = hasPathSum(root.left, targetSum - root.val);
    boolean right = hasPathSum(root.right, targetSum - root.val);
    return left | right;
}

翻转

. - 力扣(LeetCode)

前序遍历:

public TreeNode invertTree(TreeNode root) {
    if(root == null) {
        return null;
    }
    TreeNode node = root.left;
    root.left = root.right;
    root.right = node;

    invertTree(root.left);
    invertTree(root.right);

    return root;
}

后序遍历:

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;
}

层次遍历:

public TreeNode invertTree(TreeNode root) {
    if (root == null) {
        return null;
    }
    //将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素
    LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
    queue.add(root);
    while (!queue.isEmpty()) {
        //每次都从队列中拿一个节点,并交换这个节点的左右子树
        TreeNode tmp = queue.poll();
        TreeNode left = tmp.left;
        tmp.left = tmp.right;
        tmp.right = left;
        //如果当前节点的左子树不为空,则放入队列等待后续处理
        if (tmp.left != null) {
            queue.add(tmp.left);
        }
        //如果当前节点的右子树不为空,则放入队列等待后续处理
        if (tmp.right != null) {
            queue.add(tmp.right);
        }
    }

    return root;
}