瑞_力扣LeetCode_二叉搜索树相关题

文章目录

    • 说明
    • 题目 450. 删除二叉搜索树中的节点
      • 题解
        • 递归实现
    • 题目 701. 二叉搜索树中的插入操作
      • 题解
        • 递归实现
    • 题目 700. 二叉搜索树中的搜索
      • 题解
        • 递归实现
    • 题目 98. 验证二叉搜索树
      • 题解
        • 中序遍历非递归实现
        • 中序遍历递归实现
        • 上下限递归
    • 题目 938. 二叉搜索树的范围和
      • 题解
        • 中序遍历非递归实现
        • 中序遍历递归实现
        • 上下限递归实现
    • 题目 1008. 前序遍历构造二叉搜索树
      • 题解
        • 直接插入
        • 上限法
        • 分治法
    • 题目 235. 二叉搜索树的最近公共祖先
      • 题解

🙊 前言:本文章为瑞_系列专栏之《刷题》的力扣LeetCode系列,主要以力扣LeetCode网的题进行解析与分享。本文仅供大家交流、学习及研究使用,禁止用于商业用途,违者必究!

在这里插入图片描述

说明

  本文主要是配合《瑞_数据结构与算法_二叉搜索树》对二叉搜索树的知识进行提升和拓展,力扣中的树节点 TreeNode 相当于《瑞_数据结构与算法_二叉搜索树》中的 BSTNode,区别在于:

  • TreeNode(力扣)属性有:val, left, right,并未区分键值
  • BSTNode 属性有:key, value, left, right,区分了键值

  TreeNode类(力扣):

/*** 力扣用到的二叉搜索树节点*/
class TreeNode {int val;TreeNode left;TreeNode right;public TreeNode(int val) {this.val = val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}@Overridepublic String toString() {return String.valueOf(val);}
}

  BSTNode类:


/*** 二叉搜索树, 泛型 key 版本*/
public class BSTTree2<K extends Comparable<K>, V> {static class BSTNode<K, V> {// 索引(泛型),比较值K key;// 该节点的存储值(泛型)V value;// 左孩子BSTNode<K, V> left;// 右孩子BSTNode<K, V> right;public BSTNode(K key) {this.key = key;}public BSTNode(K key, V value) {this.key = key;this.value = value;}public BSTNode(K key, V value, BSTNode<K, V> left, BSTNode<K, V> right) {this.key = key;this.value = value;this.left = left;this.right = right;}}// 根节点BSTNode<K, V> root;
}

  所以力扣的 TreeNode 没有 key,比较二叉树节点用的是 TreeNode.val 属性与待删除 key 进行比较,因为力扣主要是练习题,对实际情况进行了简化

题目 450. 删除二叉搜索树中的节点

  原题链接:450. 删除二叉搜索树中的节点

  给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

  一般来说,删除节点可分为两个步骤:
  1️⃣首先找到需要删除的节点;
  2️⃣如果找到了,删除它。

  示例1:

在这里插入图片描述

输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]
解释:给定需要删除的节点值是 3,所以我们首先找到 3 这个节点,然后删除它。
一个正确的答案是 [5,4,6,2,null,null,7], 如下图所示。
另一个正确答案是 [5,2,6,null,4,null,7]。

在这里插入图片描述

  示例2:

输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]
解释: 二叉树不包含值为 0 的节点

  示例3:

输入: root = [], key = 0
输出: []

  提示:

  • 节点数的范围[0, 104].
  • -105 <= Node.val <= 105
  • 节点值唯一
  • root 是合法的二叉搜索树
  • -105 <= key <= 105

  进阶: 要求算法时间复杂度为 O(h),h 为树的高度。

题解

  删除remove(int key)方法需要考虑的情况较多。要删除某节点(称为 D),必须先找到被删除节点的父节点,这里称为 Parent,具体情况如下:

  1. 删除节点没有左孩子,将右孩子托孤给 Parent
  2. 删除节点没有右孩子,将左孩子托孤给 Parent
  3. 删除节点左右孩子都没有,已经被涵盖在情况1、情况2 当中,把 null 托孤给 Parent
  4. 删除节点左右孩子都有,可以将它的后继节点(称为 S)托孤给 Parent,设 S 的父亲为 SP,又分两种情况
    1. SP 就是被删除节点,此时 D 与 S 紧邻,只需将 S 托孤给 Parent
    2. SP 不是被删除节点,此时 D 与 S 不相邻,此时需要将 S 的后代托孤给 SP,再将 S 托孤给 Parent

  删除本身很简单,只要通过索引查找到该节点删除即可,但是,由于需要料理后事,所以想要做好删除操作,需要处理好“托孤”操作。

递归实现
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public TreeNode deleteNode(TreeNode node, int key) {if (node == null) {return null;}if (key < node.val) { // 没找到,继续递归调用node.left = deleteNode(node.left, key);return node;}if (node.val < key) { // 没找到,继续递归调用node.right = deleteNode(node.right, key);return node;}if (node.left == null) { // 情况1 - 只有右孩子return node.right;}if (node.right == null) { // 情况2 - 只有左孩子return node.left;}TreeNode s = node.right; // 情况3 - 有两个孩子while (s.left != null) {s = s.left;}s.right = deleteNode(node.right, s.val);s.left = node.left;return s;}
}

题目 701. 二叉搜索树中的插入操作

  原题链接:701. 二叉搜索树中的插入操作

  给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

  注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果

  示例1:

在这里插入图片描述

输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]
解释:另一个满足题目要求可以通过的树是:

在这里插入图片描述

  示例 2:

输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]

  示例 3:

输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]

  提示:

  • 树中的节点数将在 [0, 104]的范围内。
  • -108 <= Node.val <= 108
  • 所有值 Node.val 是 独一无二 的。
  • -108 <= val <= 108
  • 保证 val 在原始BST中不存在。

题解

  分为两种情况:

  1️⃣ key在整个树中已经存在,新增操作变为更新操作,将旧的值替换为新的值
  2️⃣ key在整个树中未存在,执行新增操作,将key value添加到树中

  由于题目中的前提是:保证 val 在原始BST中不存在。因此只需考虑新增情况,不会出现更新情况

递归实现
  • 若找到 key,走 else 更新找到节点的值
  • 若没找到 key,走第一个 if,创建并返回新节点
    • 返回的新节点,作为上次递归时 node 的左孩子或右孩子
/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public TreeNode insertIntoBST(TreeNode node, int val) {if (node == null) {return new TreeNode(val);}if (val < node.val) {node.left = insertIntoBST(node.left, val);} else if (node.val < val) {node.right = insertIntoBST(node.right, val);}return node;}
}

  此处return node返回当前节点会多出一些额外的赋值动作。如下面这颗二叉搜索树,1作为插入节点,1和2通过node.left = insertIntoBST(node.left, val);建立父子关系返回,这是有必要的,但是由于递归,2和5也会通过node.left = insertIntoBST(node.left, val);建立父子关系,这样就是没必要的(因为5和2原本就存在父子关系),如果树的深度很大,那就会浪费很多性能。

        5/ \2   6\   \1    3   7

题目 700. 二叉搜索树中的搜索

  原题链接:700. 二叉搜索树中的搜索

  给定二叉搜索树(BST)的根节点root和一个整数值val

  你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null

  示例1:

在这里插入图片描述

输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]

  示例2:

在这里插入图片描述

输入:root = [4,2,7,1,3], val = 5
输出:[]

  提示:

  • 树中节点数在 [1, 5000] 范围内
  • 1 <= Node.val <= 107
  • root 是二叉搜索树
  • 1 <= val <= 107

题解

递归实现
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public TreeNode searchBST(TreeNode node, int val) {if (node == null) {return null;}if (val < node.val) {return searchBST(node.left, val);} else if (node.val < val) {return searchBST(node.right, val);} else {return node;}}
}

题目 98. 验证二叉搜索树

  原题链接:98. 验证二叉搜索树

  给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

  有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

  示例 1:

在这里插入图片描述

输入:root = [2,1,3]
输出:true

  示例 2:

在这里插入图片描述

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

  提示:

  • 树中节点数目范围在[1, 104] 内
  • -231 <= Node.val <= 231 - 1

题解

  合法的二叉搜索树即左边的都更小,右边的都更大,如下,第一个的树为合法二叉搜索树,而第二、三(等于也是非法的)均不合法

         4			  5		      1/   \		/   \			\2     6	   4     6            1/ \			   /   \	1   3			  3     7

  思路:利用二叉树中序遍历的特性(遍历后是升序的结果),所以遍历的结果一定是一个由小到大的结果,以此判断是否合法

瑞:关于二叉树中序遍历,可以参考《瑞_数据结构与算法_二叉树》

中序遍历非递归实现
public boolean isValidBST(TreeNode root) {TreeNode p = root;LinkedList<TreeNode> stack = new LinkedList<>();// 代表上一个值long prev = Long.MIN_VALUE;while (p != null || !stack.isEmpty()) {if (p != null) {stack.push(p);p = p.left;} else {TreeNode pop = stack.pop();// 处理值,上一个值大于等于当前值的时候是非法二叉搜索树if (prev >= pop.val) {return false;}// 更新值prev = pop.val;p = pop.right;}}return true;
}

  记录 prev 需要用 long,否则若测试用例中最小的节点为 Integer.MIN_VALUE 则测试会失败

中序遍历递归实现

  使用递归的时候为避免性能浪费,可以进行剪枝操作。要注意在递归的时候不能用 Long 或 long,因为它们都是局部变量且不可变,因此每次赋值时,并不会改变其它方法调用时的 prev,所以要把 prev 设置为全局变量

    // 全局变量记录 prevlong prev = Long.MIN_VALUE;public boolean isValidBST(TreeNode node) {if (node == null) {return true;}boolean a = isValidBST(node.left);// 剪枝if (!a) {return false;}// 处理值if (prev >= node.val) {return false;}prev = node.val;return isValidBST(node.right);}

  瑞:以上代码在力扣中运行的时间是0ms,竟然优于中序遍历非递归实现的1ms,这里理论上说不过去,毕竟递归应该更耗费性能,但有可能由于力扣对栈的使用有自己的判定方式,所以可能造成这样的运行结果,但是理论上应该是非递归效率更好

上下限递归

  可以对每个节点增加上下限,使用上限下限来递归判断。

  • 根节点的下限是-∞,上限是+∞
  • 根节点的左孩子的下限是-∞,上限是根节点值
  • 根节点的右孩子的下限是根节点值,上限是上限是+∞
  • 以此递归
public boolean isValidBST(TreeNode node) {return doValid(node, Long.MIN_VALUE, Long.MAX_VALUE);
}private boolean doValid(TreeNode node, long min, long max) {if (node == null) {return true;}if (node.val <= min || node.val >= max) {return false;}return doValid(node.left, min, node.val) && doValid(node.right, node.val, max);
}
  • 设每个节点必须在一个范围内: ( m i n , m a x ) (min, max) (min,max),不包含边界,若节点值超过这个范围,则返回 false
  • 对于 node.left 范围肯定是 ( m i n , n o d e . v a l ) (min, node.val) (min,node.val)
  • 对于 node.right 范围肯定是 ( n o d e . v a l , m a x ) (node.val, max) (node.val,max)
  • 一开始不知道 min,max 则取 java 中长整数的最小、最大值
  • 本质是前序遍历 + 剪枝

题目 938. 二叉搜索树的范围和

  原题链接:938. 二叉搜索树的范围和

  给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和。

  示例 1:

在这里插入图片描述

输入:root = [10,5,15,3,7,null,18], low = 7, high = 15
输出:32

  示例 2:

在这里插入图片描述

输入:root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10
输出:23

  提示:

  • 树中节点数目在范围 [1, 2 * 104] 内
  • 1 <= Node.val <= 105
  • 1 <= low <= high <= 105
  • 所有 Node.val 互不相同

题解

中序遍历非递归实现

  思路:使用中序遍历(升序)判断,如果在范围内就进行累加,最终返回和。使用中序遍历的特性,当遍历到累加区间上限的时候,遍历即可停止。

    public int rangeSumBST(TreeNode node, int low, int high) {TreeNode p = node;LinkedList<TreeNode> stack = new LinkedList<>();int sum = 0;while (p != null || !stack.isEmpty()) {if (p != null) {stack.push(p);p = p.left;} else {TreeNode pop = stack.pop();// 处理值if (pop.val > high) {// 提前结束遍历break;}if (pop.val >= low) {sum += pop.val;}p = pop.right;}}return sum;}

虽然此解法思路很好想到,但是放到力扣上跑就发现,需要耗时4ms,还有很大的优化空间,因为只筛选了上限而不能跳过下限。

中序遍历递归实现

  将上个方案修改为递归实现,耗时减少

public int rangeSumBST(TreeNode node, int low, int high) {if (node == null) {return 0;}int a = rangeSumBST(node.left, low, high);int b = 0;if (node.val >= low && node.val <= high) {b = node.val;}return a + b + rangeSumBST(node.right, low, high);
}

虽然在力扣上提升到了1ms,但是仍然有更好的方式

上下限递归实现

  思路:中序遍历的性能提升瓶颈在于上限无法被跳过,那就使用上下限递归的方案,当递归到上限的时候,其递归就可以结束,同理递归到下限的时候,其递归就可以结束。

/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public int rangeSumBST(TreeNode node, int low, int high) {if (node == null) {return 0;}// 此分支只需考虑它右子树的累加结果if (node.val < low) {return rangeSumBST(node.right, low, high);}// 此分支只需考虑它左子树的累加结果if (node.val > high) {return rangeSumBST(node.left, low, high);}// node.val 在范围内,需要把当前节点的值加上其左右子树的累加结果return node.val +rangeSumBST(node.left, low, high) +rangeSumBST(node.right, low, high);}
}

在力扣上运行只耗时 0 ms

题目 1008. 前序遍历构造二叉搜索树

  原题链接:1008. 前序遍历构造二叉搜索树

  给定一个整数数组,它表示BST(即 二叉搜索树 )的 先序遍历 ,构造树并返回其根。

  保证 对于给定的测试用例,总是有可能找到具有给定需求的二叉搜索树。

  二叉搜索树 是一棵二叉树,其中每个节点, Node.left 的任何后代的值 严格小于 Node.val , Node.right 的任何后代的值 严格大于 Node.val

  二叉树的 前序遍历 首先显示节点的值,然后遍历Node.left,最后遍历Node.right

  示例1:

在这里插入图片描述

输入:preorder = [8,5,1,7,10,12]
输出:[8,5,10,1,7,null,12]

  示例2:

输入: preorder = [1,3]
输出: [1,null,3]

  提示:

  • 1 <= preorder.length <= 100
  • 1 <= preorder[i] <= 108
  • preorder 中的值 互不相同

题解

  注意前提,前序遍历数组的长度是大于等于1的(肯定存在根节点),且数组中的值互不相同(只需考虑插入的情况而不需要考虑更新的情况)

直接插入

  思路:根据前序遍历的结果,可以唯一地构造出一个二叉搜索树。所以可以从左向右,以此插入节点思想的思想构建二叉树。

  如前序遍历结果为:[8,5,1,7,10,12]。那么8作为根节点,5比8(根节点)小,查看到8的左孩子为空,插入。继续遍历插入1,1比8(根节点)小,但是8的左子树已经有了5,1比5小,所以查看到5的左孩子为空,插入。继续遍历插入7,7比8(根节点)小,但是8的左子树已经有了5,7比5大,所以查看到8的右孩子为空,插入。继续遍历10,10比8(根节点)大,查看到8的右孩子为空,插入。继续遍历12,12比8(根节点)大,但是8的右子树已经有了10,12比10大,查看10的右孩子为空,插入。遍历结束,二叉搜索树构造完成,如下:

            8/ \5   10/ \   \1   7  12
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public TreeNode bstFromPreorder(int[] preorder) {TreeNode root = insert(null, preorder[0]);for (int i = 1; i < preorder.length; i++) {insert(root, preorder[i]);}return root;}private TreeNode insert(TreeNode node, int val) {if (node == null) {return new TreeNode(val);}if (val < node.val) {node.left = insert(node.left, val);} else if (node.val < val) {node.right = insert(node.right, val);}return node;}
}

虽然耗时为0ms,但是时间复杂度为 O(n long n),可以优化

上限法

  思路:
  1.遍历数组中每一个值,根据值创建节点
    每个节点若成功创建则加上:左孩子上限,右孩子上限
  2.处理下一个值时,如果超过此上限,那么
    将 null 作为上个节点的孩子
    不能创建节点对象
    直到不超过上限位置
  3.重复1.2.步骤

            8/ \5   10/ \   \1   7  12

  如上二叉搜索树的前序遍历结果为:[8,5,1,7,10,12]。
  则根节点8左限为8,右限为MAX。
  继续遍历5,5左限为5,右限为8。
  继续遍历1,1左限为1,右限为5。
  继续遍历7,7超过1的左限1,不能作为1的左孩子,所以此时设置1的左孩子为null,7超过1的右限5,所以7也不能作为1的右孩子,此时设置1的右孩子为null,此时1的左右孩子均为null,代表1结束,此时建立1和5的父子关系,继续判断,7没有超过5的右限8,所以7可以作为5的右孩子,所以7的左限为7右限位8。
  继续遍历10,10超过7的左限7,不能作为7的左孩子,所以此时设置7的左孩子为null,10超过7的右限8,所以10也不能作为7的右孩子,此时设置7的右孩子为null,此时7的左右孩子均为null,代表7结束,此时建立7和5的父子关系,此时5有左孩子1,右孩子7,5节点构建完成,将10与5的上一个节点8继续判断,10超过8的左限8,10小于8的右限MAX,所以10可以作为8的右孩子,此时8的左孩子为5右孩子为10,8构建完成,10的左限为10,右限为MAX。
  继续遍历12,12超过10的左限10,不能作为10的左孩子,此时设置10的左孩子为null,12没有超过10的右限MAX,所以10可以作为12的右孩子,此时10的左孩子为null右孩子为12,10构建完成,遍历此时也结束,二叉搜索树构建完成。

public TreeNode bstFromPreorder(int[] preorder) {return insert(preorder, Integer.MAX_VALUE);
}int i = 0;
private TreeNode insert(int[] preorder, int max) {if (i == preorder.length) {return null;}int val = preorder[i];
//    System.out.println(val + String.format("[%d]", max));if (val > max) {return null;}TreeNode node = new TreeNode(val);i++;node.left = insert(preorder, node.val); node.right = insert(preorder, max);     return node;
}

依次处理 prevorder 中每个值, 返回创建好的节点或 null 作为上个节点的孩子

  1. 如果超过上限, 返回 null
  2. 如果没超过上限, 创建节点, 并将其左右孩子设置完整后返回
    • i++ 需要放在设置左右孩子之前,意思是从剩下的元素中挑选左右孩子

时间复杂度为 O(long n)

分治法

  思想:利用前序遍历的特性(第一个元素为根节点,往后小于根节点的是左子树,大于根节点的是右子树,区分出根左右),通过递归的方式,不断拆解,缩小区域,直到区域内没有元素则划分完成。

            8/ \5   10/ \   \1   7  12

  如上二叉搜索树的前序遍历结果为:[8,5,1,7,10,12]

  • 刚开始 8, 5, 1, 7, 10, 12,方法每次执行,确定本次的根节点和左右子树的分界线
  • 第一次确定根节点为 8,左子树 5, 1, 7,右子树 10, 12
  • 对 5, 1, 7 做递归操作,确定根节点是 5, 左子树是 1, 右子树是 7
  • 对 1 做递归操作,确定根节点是 1,左右子树为 null
  • 对 7 做递归操作,确定根节点是 7,左右子树为 null
  • 对 10, 12 做递归操作,确定根节点是 10,左子树为 null,右子树为 12
  • 对 12 做递归操作,确定根节点是 12,左右子树为 null
  • 递归结束,返回本范围内的根节点
public TreeNode bstFromPreorder(int[] preorder) {return partition(preorder, 0, preorder.length - 1);
}private TreeNode partition(int[] preorder, int start, int end) {if (start > end) {return null;}TreeNode root = new TreeNode(preorder[start]);int index = start + 1;while (index <= end) {if (preorder[index] > preorder[start]) {break;}index++;}// index 就是右子树的起点root.left = partition(preorder, start + 1, index - 1);root.right = partition(preorder, index, end);return root;
}

时间复杂度为 O(n long n)

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

  原题链接:235. 二叉搜索树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/description/

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

  百度百科中最近公共祖先的定义为:“对于有根树 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, 因为根据定义最近公共祖先节点可以为节点本身。

  说明:

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

题解

  思路:若 p,q 在 ancestor 的两侧(含p,q自身),则 ancestor 就是它们的最近公共祖先。

        __ 6 __/       \2         8/ \       / \0   4     7   9/ \3   5

  如上二叉搜索树:假设p为2,q为8,则6为它们的最近公共祖先。
  再如4和5,都在6的左子树,属于一侧,所以6就不是它们公共祖先。继续往6的左子树下遍历2,4和5在2的右子树,也属于一侧,所以2不是它们的公共祖先。继续往2的右子树下遍历4,4等于4,5大于4,则可认为不在同一侧,即4和5在4的两侧,4为4和5的最近公共祖先。

/*** 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) {TreeNode ancestor = root;// 条件成立表示在同一侧while (ancestor.val > p.val && ancestor.val > q.val ||ancestor.val < p.val && ancestor.val < q.val) {if (ancestor.val > p.val) {ancestor = ancestor.left;} else {ancestor = ancestor.right;}}return ancestor;}
}



本文是博主的粗浅理解,可能存在一些错误或不完善之处,如有遗漏或错误欢迎各位补充,谢谢

  如果觉得这篇文章对您有所帮助的话,请动动小手点波关注💗,你的点赞👍收藏⭐️转发🔗评论📝都是对博主最好的支持~


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/438080.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序(二十二)获取全局实例

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.全局实例的定义位置 2.全局实例中数据的修改方法 源码&#xff1a; app.js App({//数据可以包括在第二级globalData:{userInfo:null,token:1243,userInfo:null},//globalData并不是关键词&#xff0c;数据可以…

Redis系列-数据结构篇

数据结构 string&#xff08;字符串&#xff09; redis的字符串是动态字符串&#xff0c;类似于ArrayList&#xff0c;采用预分配冗余空间的方式减少内存的频繁分配。 struct SDS<T>{ T capacity; T len; byte flags; byte[] content; } 当字符串比较短时&#xff0c…

matlab GUI实现PID控制器参数配置

1、内容简介 略 39-可以交流、咨询、答疑 2、内容说明 略 3、 基于GUI的PID研究 本例子中设计一个PID控制器来研究不同参数对输出结果的影响&#xff0c;PID控制器由比例单元 P、积分单元 I 和微分单元 D 组成。PID 控制器是一个在工业控制应用中常见的反馈回路部件&…

基于springboot网吧管理系统源码和论文

随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&#xf…

点击查看 Milvus 社区十大关键词(上)

GitHub Star 突破 25,000、集众多特性于一体的 2.3.0 版本重磅发布、Docker Hub Milvus 镜像下载量突破 1000 万……过去一年&#xff0c;Milvus 社区取得了很多激动人心的进展&#xff0c;而 Milvus 技术交流群作为众多开发者交流分享的重要阵地&#xff0c;今年的规模也突破了…

渗透测试(12)- WireShark 网络数据包分析

目录 1、WireShack 简介 2、WireShark 基本使用方法 3、 WireShack 抓包分析 3.1 Hypertext Transfer Protocol (应用层) 3.2 Transmission Control Protocol (传输层) 3.3 Internet Protocol Version 4(网络层) 3.4 Ethernet Il (链路层): 数据链路层以太网头部信息 …

c#之构值类型和引用类型

值类型:(整数/bool/struct/char/小数) 引用类型:(string/ 数组 / 自定义的类 / 内置的类) 值类型只需要一段单独的内存,用于存储实际的数据 引用类型需要两段内存(第一段存储实际的数据,他总是位于 堆中第二段是一个引用,指向数据在堆中的存放位置) 当使用引用类型赋值的时…

AS自治系统中的路由协议---RIP、OSPF、BGP

一、AS --- 自治系统 将网络分块管理 --- 由单一的机构或组织所管理 的一系列IP网络及其设备的集合 AS的管理&#xff1a;为了方便对AS进行管理&#xff0c;我们给AS设计了一个编号称为AS 号 --- 16位二进制构成 --- 0 - 65535 ---- 目前也存在拓展版的AS 号 --- 32位二进制构…

交换机跨VLAN交换数据ip跳转分析(不一定对)

在网上看到这样一个实验&#xff1a; 交换机1、交换机2分别连接到一台防火墙上&#xff0c;要求使VLAN 2、VLAN3、VLAN5、VLAN6中的终端可互相访问。 拓补 参考链接 【数通网络交换基础梳理2】三层设备、网关、ARP表、VLAN、路由表及跨网段路由下一跳转发原理_网管型交的机…

镀膜玻璃行业研究:未来市场潜力巨大

建筑是深加工玻璃最主要的应用领域&#xff0c;建筑玻璃上游主要是石英砂、纯碱、石灰石等原材料包括将平板玻璃进行深加工形成的中空玻璃、 夹层玻璃、镀膜玻璃、 钢化玻璃、 压花玻璃等。 玻璃作为透明材料被广泛应用于建筑中&#xff0c;由于玻璃制品是一种良好的热导性建筑…

【机器学习】强化学习(七)-策略梯度算法-REINFORCE 训练月球着陆器代理(智能体)...

概 述 月球着陆器代理是一个模拟飞行器在月球表面着陆的环境&#xff0c;它有八个连续的状态变量&#xff0c;分别是水平坐标、垂直坐标、水平速度、垂直速度、角度、角速度、腿1触地、腿2触地。它有四个离散的动作&#xff0c;分别是什么都不做、发动左方向引擎、发动主引擎、…

C语言-指针的基本知识(上)

一、关于内存 存储器&#xff1a;存储数据器件 外存 外存又叫外部存储器&#xff0c;长期存放数据&#xff0c;掉电不丢失数据 常见的外存设备&#xff1a;硬盘、flash、rom、u盘、光盘、磁带 内存 内存又叫内部存储器&#xff0c;暂时存放数据&#xff0c;掉电数据…