剑指offer_20230728

xccx-0824 / 2023-07-28 / 原文

剑指 Offer 68 - II. 二叉树的最近公共祖先

题目说明

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

解题思路1:HashSet&HashMap

  1. 可以先对树进行遍历,记录下每个节点的父节点,root节点的父节点记为null,存储在HashMap中
  2. 节点p一层一层的往上爬,追溯所有的祖宗节点并记录在set中
  3. 对q节点的祖宗节点一层层往上爬,如果在set中出现了说明是他们的最近公共祖先

解题思路2:

设定一个函数findNode对节点进行查找,找到则返回true,没有则返回false。如果第一次在左右子树都分别找到了p和q,则说明该节点是最近的祖宗节点,此后不做更新。

在lowestCommonAncestor方法中也使用中序遍历从底下往上找

但是这个方法的问题就在于每进行一次lowestCommonAncestor都要进行findNode,会导致性能不佳

解题思路3:dfs(最好的)

同样用一个节点ancestor来记录最终找到的最近公共祖先,但只通过dfs来进行遍历。

同样也是遍历左右两边,用boolean记录左右子树有没有找到节点。但是在判定以及返回上非常的有意思

if ((left && right) || (root.val == p.val || root.val == q.val) && (left || right)) {
    ancestor = root;
}

return left || right || root.val == p.val || root.val == q.val;

如果左右子树都是true说明p和q分别在左右子树出现了,则当前节点肯定是最近的祖先节点

如果是root.val和p或q的值相等且左右子树有返回true,说明祖宗节点就是p或者q(也就是当前节点)

在返回时,如果left和right为true说明p或q在这个节点的子树下,后两个则说明直接找到了就在当前节点

剑指 Offer 36. 二叉搜索树与双向链表

题目说明

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

解题思路1:链表记录

直接中序遍历获取到所有节点并且记录在ArrayList中,然后再对ArrayList进行遍历来进行左右指针的变换

解题思路2:

通过中序遍历,用head和pre分别记录头节点(只更新一次)和当前节点的上一个节点

public void dfs(Node node) {
    if (node == null) {
    return;
    }

    dfs(node.left);
    if (pre != null) {
    pre.right = node;
    }else {
    head = node;
    }
    node.left = pre;
    pre = node;
    dfs(node.right);
}

一次只针对pre和cur(代码中为node)两个节点进行更新操作,根据二叉搜索树特性采用中序遍历可以保证获得pre一定是cur的上一位

在遍历结束之后,对head和pre(此时定位到末尾了)进行操作,最终返回head

剑指 Offer 26. 树的子结构

题目说明

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

解题思路

  1. 首先在主方法中判定A和B是否为null,是的话直接返回false
  2. 在遍历方法中,如果B为null则说明找到子树了,如果A为null则说明没找到。如果当前两个节点相等则遍历AB的左子树和右子树
  3. 其余情况返回false
public boolean traversal(TreeNode A, TreeNode B) {
        if (B == null) {
            return true;
        }
        if (A == null) {
            return false;
        }
        
        if (A.val == B.val) {
            if (traversal(A.left, B.left) && traversal(A.right, B.right)) {
                return true;
            }
        }
        return false;
    }