【剑指 Offer】第 19 天

136 阅读4分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、前言

刷题啊!!!

开始刷 “剑指 Offer” 31天。刷完时间:2022.3.6 ~ 2022.3.20。



二、题目

题意:

  • 求1+2+…+n
  • 二叉搜索树的最近公共祖先
  • 二叉树的最近公共祖先

(1)剑指 Offer 64. 求1+2+…+n

题目描述


求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

示例 1:

输入: n = 3
输出: 6
示例 2:

输入: n = 9
输出: 45

限制:1 <= n <= 10000

题解


方法一:公式,平均计算,但会使用 乘法和除法。

class Solution {
    public int sumNums(int n) {
        return (1 + n) * n / 2;
    }
}

方法二:

  • for 循环:来加数,可以使用 递归 替代。
  • 断路法:来终止递归。
class Solution {
    int res = 0;
    public int sumNums(int n) {
        boolean flag = n > 1 && sumNums(n - 1) > 0;
        res += n;
        return res;
    }
}

(2)剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

题目描述


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

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

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

题解


思路步骤:

  1. 目标值均大于 或 小于根节点,继续迭代下去
  2. 目标值不均大于或小于根节点,则返回根节点

方法:

  1. 递归方法
  2. 迭代方法
public class LeetCode_235 {

    // 方法一:递归方法
    // Time: O(h), Space: O(h), Faster: 100%
    public TreeNode lcaRecursive(TreeNode root, TreeNode p, TreeNode q) {
        if (p.val < root.val && q.val < root.val)
            return lcaRecursive(root.left, p, q);
        else if (p.val > root.val && q.val > root.val)
            return lcaRecursive(root.right, p, q);
        else return root;
    }

    // 方法二:迭代方法
    // Time: O(h), Space: O(1), Faster: 12.12%
    public TreeNode lcaIterative(TreeNode root, TreeNode p, TreeNode q) {
        while (root != null) {
            if (p.val < root.val && q.val < root.val) root = root.left;
            else if (p.val > root.val && q.val > root.val) root = root.right;
            else return root;
        }
        return null;
    }
}

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

题目描述


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

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

例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

题解


方法二,递归方法,详细步骤如下:

  1. 如果当前节点为空,或者等于目标节点 pq,则返回当前节点。
  2. 否则递归到左右子树上进行处理,返回值分别为 leftright
  3. 如果 leftright 非空,则说明在左右子树上各找到一个节点,于是当前的根节点就是最近公共祖先
    • 如果 leftright 只有一个非空,则返回那个非空的节点。
    • 如果都为空,就返回空指针。

总而言之:先找对应的节点,再逐级到父节点比对。

举个栗子:

查找节点 4 和 3 的最近公共祖先
      1
    /   \
   2     3
  / \     \
 4   5     6
 
节点 4 和 3 的最近公共祖先是节点 1;


实操步骤:
1. 递归到节点 2,对比左右节点,返回节点 4
   1. 查到目标左节点 4, 返回节点 4
   2. 没有查到目标节点,返回 NULL
   
2. 递归到节点 3, 找到目标节点,返回节点 3

3. 在根节点 1 汇总,发现左右节点都存在,则返回根节点
public class LeetCode_236 {

    // 方法一:求出路径后,进行逐一比对
    // Time: O(n), Space: O(n), Faster: 29.20%
    public TreeNode lcaWithPath(TreeNode root, TreeNode p, TreeNode q) {
        List<TreeNode> ppath = new ArrayList<>();
        List<TreeNode> qpath = new ArrayList<>();
        search(root, p, ppath);
        search(root, q, qpath);
        int i = 0, len = Math.min(ppath.size(), qpath.size());
        while (i < len && ppath.get(i) == qpath.get(i)) ++i;
        return ppath.get(i-1);
    }

    private boolean search(TreeNode root, TreeNode node, List<TreeNode> path) {
        if (root == null) return false;
        path.add(root);
        if (root == node) return true;
        boolean ret = search(root.left, node, path) || search(root.right, node, path);
        if (ret) return true;
        path.remove(path.size()-1);
        return false;
    }

    // 方法二:递归方法
    // Time: O(n), Space: O(n), Faster: 99.91%
    public TreeNode lcaExtend(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root == p || root == q) return root;
        TreeNode left = lcaExtend(root.left, p, q);
        TreeNode right = lcaExtend(root.right, p, q);
        if (left != null && right != null) return root;
        else if (left != null) return left;
        else return right;
    }
}