Leetcode 235. 二叉搜索树的最近公共祖先

237 阅读4分钟

Leetcode 235. 二叉搜索树的最近公共祖先

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

1、题目📑

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

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

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

img

实例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, 因为根据定义最近公共祖先节点可以为节点本身。

说明

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉搜索树中。

2、思路🧠

方法一:递归遍历每个节点

分情况将出现的三种可能的情况列出来

  1. root == null || root == p || root == q,直接返回根节点
  2. 递归左树,右树
    • 如果左树为空,说明这两个节点在root结点的右树,只需要返回右子树查找的结果
    • 如果右树为空,说明这两个节点在root结点的左树,只需要返回左子树查找的结果
    • 如果左树和右树都不为空,说明这两个节点一个在 root 的左树上,一个在cur的右子树上,只需要返回 root 结点。

方法二:递归

题目的要求是让求二叉搜索树的最近公共祖先,而需要注意到题目中的关键字二叉搜索树,做这道题的关键需要了解概念及特点,二叉搜索树的是 左子树的所有节点都小于当前节点,右子树的所有节点都大于当前节点,并且每棵子树都具有上述特点

在第一次的提交没有考虑到如果p q是根节点的化相乘等于0的情况

  • 如果两个节点值都小于根节点,说明两个节点都在根节点的左树上
  • 如果两个节点值都大于根节点,说明两个节点都在根节点的右树上
  • 如果一个节点值大于根节点,另一个节点值小于根节点,说明一个在根节点的左树,另一个在根节点的右树,所以根节点就为这两个节点的最近公共祖先节点。

废话少说~~~~~上代码!

3、代码👨‍💻

第一次commit AC

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root ==q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right,p , q);
        if(left == null) return right;
        if(right == null) return left;
        return root;
    }
}

时间复杂度:O(N ) 其中 N 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

空间复杂度:O(1)

第二次commit AC

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //这里少考虑一种情况,如果p q是根节点的化相乘等于0的情况
        if((root.val - p.val) * (root.val - q.val) <= 0) {
            //说明在两侧,直接返回根节点
            return root;
        }
        return lowestCommonAncestor(root.val - p.val < 0 ? root.right : root.left, p, q);
    }
}

时间复杂度:O(N ) 其中 N 为二叉树节点的个数。二叉树的遍历中每个节点会被访问一次且只会被访问一次。

空间复杂度:O(1)

image-20220411204438224

4、总结

该题目的是对二叉搜索树结构、概念、特点的理解与遍历,需要掌握先序、中序、后序的基本概念以及遍历。

树的先序、中序、后序模板:

package com.cz.Tree;

import java.util.Stack;

/**
 * @ProjectName: Data_structure
 * @Package: com.cz.Tree
 * @ClassName: UnRecursiveTraversalTree
 * @Author: 张晟睿
 * @Date: 2022/3/20 16:06
 * @Version: 1.0
 */
public class UnRecursiveTraversalTree {
    public static void main(String[] args) {
        Node1 head = new Node1(1);
        head.left = new Node1(2);
        head.right = new Node1(3);
        head.left.left = new Node1(4);
        head.left.right = new Node1(5);
        head.right.left = new Node1(6);
        head.right.right = new Node1(7);

        pre(head);
        System.out.println("========");
        middle(head);
        System.out.println("========");
        post(head);
        System.out.println("========");
    }
    public static class Node1 {
        public int value;
        public Node1 left;
        public Node1 right;

        public Node1(int val) {
            value = val;
        }
    }

    public static void pre(Node1 head) {
        System.out.println("先序遍历:");
        Stack<Node1> s = new Stack<>();
        if(head != null) {
            s.push(head);
            while(!s.isEmpty()) {
               Node1 node = s.pop();
                System.out.print(node.value + " ");
                if(node.right != null) s.push(node.right);
                if(node.left != null) s.push(node.left);
            }
        }
        System.out.println();
    }

    public static void middle(Node1 head){
        System.out.println("中序遍历:");
        if (head != null) {
            Stack<Node1> s = new Stack<>();
            while(!s.isEmpty() || head != null) {
                //步骤1:如果头结点不为空的话,一直向左边执行
                if (head != null) {
                    s.push(head);
                    head = head.left;
                }else {//根节点打印后,来到右树,继续执行步骤1
                    head = s.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
            System.out.println();
        }
    }

    public static void post(Node1 head){
        System.out.println("后序遍历:");
        if(head != null) {
            Stack<Node1> s = new Stack<>();
            s.push(head);
            Node1 c = null; //指向栈顶的某个元素的位置
            while(!s.isEmpty()) {
                c = s.peek();
                //判断c左孩子 是否已经处理过
                if(c.left != null && head != c.left && head != c.right) {
                    s.push(c.left);
                    //判断c右孩子 是否已经处理过
                }else if(c.right != null && head != c.right) {
                    s.push(c.right);
                }else {
                    System.out.print(s.pop().value+" ");
                    head = c;   //head用来记录上次打印的内容
                }
            }
            System.out.println();
        }
    }
}

❤️‍来自专栏《LeetCode基础算法题》欢迎订阅❤️‍

厂长写博客目的初衷很简单,希望大家在学习的过程中少走弯路,多学一些东西,对自己有帮助的留下你的赞赞👍或者关注➕都是对我最大的支持,你的关注和点赞给厂长每天更文的动力。

对文章其中一部分不理解,都可以评论区回复我,我们来一起讨论,共同学习,一起进步!

原题链接:235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode) (leetcode-cn.com)