跟着卡哥学算法Day 20:二叉树常见题目7

2010 字
10 分钟
跟着卡哥学算法Day 20:二叉树常见题目7

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

力扣链接 🌟🌟

题目描述#

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

百度百科中最近公共祖先的定义为:“对于有根树 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 为不同节点且均存在于给定的二叉搜索树中。
// 输出 树根节点
// 6
// / \
// 2 8
// / \ / \
// 0 4 7 9
// / \
// 3 5

解题思路#

二叉搜索树

  • p 和 q 值都小于当前节点值时,p 和 q 的最近公共祖先就在左子树

  • p 和 q 值都大于当前节点值时,p 和 q 的最近公共祖先就在右子树

  • 否则,当前节点就是 p 和 q 的最近公共祖先(此时 p 和 q 分布在当前节点的两侧,或其中一个等于当前节点)

    如上述二叉搜索树:如果 2 处于[p, q]之间时,此时 p 就在节点 2 的左子树,q 在节点 2 的右子树,那么 2 就一定是 p 和 q 的最近公共祖先。

递归三部曲:

  1. 明确递归函数的参数和返回值

    • 参数 1:当前节点
    • 参数 2:p
    • 参数 3
    • 返回值:最近公共节点
  2. 明确终止条件

    • 当节点为 null 返回 null
  3. 确定单层递归逻辑

    • p 和 q 值都大于 当前节点值时,继续遍历左子树

    • p 和 q 值都小于 当前节点值时,继续遍历右子树

    • 否则,返回当前节点

      if (cur.val > p.val && cur.val > q.val)
      if (cur.val < p.val && cur.val < q.val)
      // 遍历左子树
      // 遍历右子树
      return cur
function lowestCommonAncestor(root, p, q) {
if (!root) return null
if (root.val > p.val && root.val > q.val) {
return lowestCommonAncestor(root.left, p, q)
} else if (root.val < p.val && root.val < q.val) {
return lowestCommonAncestor(root.right, p, q)
} else {
return root
}
}
// 迭代法
var lowestCommonAncestor = function (root, p, q) {
if (!root) return null
while (root) {
if (root.val < p.val && root.val < q.val) {
return lowestCommonAncestor(root.right, p, q)
}
if (root.val > p.val && root.val > q.val) {
return lowestCommonAncestor(root.left, p, q)
}
return root
}
}

701.二叉搜索树中的插入操作 🌟🌟#

力扣链接 🌟🌟

题目描述#

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

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

示例:

// 给定二叉搜索树:
// 4
// / \
// 2 7
// / \
// 1 3
// 和 插入的值:5
// 返回二叉树
// 4
// / \
// 2 7
// /\ /
// 1 3 5
// 或者这个树也是有效的
// 5
// / \
// 2 7
// / \
// 1 3
// \
// 4

提示:

  • 给定的树上的节点数介于 0 和 10^4 之间
  • 每个节点都有一个唯一整数值,取值范围从 0 到 10^8
  • -10^8 <= val <= 10^8
  • 新值和原始二叉搜索树中的任意节点值都不同

#

解题思路#

递归#

递归三部曲:

  1. 确定递归函数的参数和返回值

    • 参数 1:当前节点
    • 参数 2:要插入的值
    • 返回值:root
  2. 确定终止条件

    如果当前节点为 null,则根据值创建新节点

    if (!root1) return new TreeNode(val)
  3. 确定单层递归逻辑

    • 当前节点值大于 val,继续遍历左子树
    • 当前节点值小于 val,继续遍历右子树
    • 否则,返回返回当前节点
function insertIntoBST(root, val) {
const setInOrder = (root, val) => {
if (!root) return new TreeNode(val)
if (root.val > val) root.left = setInOrder(root.left, val)
if (root.val < val) root.right = setInOrder(root.right, val)
return root
}
return setInOrder(root, val)
}

迭代#

步骤:

  1. 记录当前节点 root 以及父节点 parent(初始化时定义)
  2. 循环遍历当前节点,比较当前节点值和插入值
  3. 如果当前节点不为空,更新当前节点为左或右子节点,同时更新父节点
  4. 如果当前节点为空,根据父节点的值和插入值的大小,决定插入到左还是右
  5. 返回 root
function insertIntoBST(root, val) {
if (!root) {
return new TreeNode(val)
} else {
let parent = null
let cur = root
while (cur) {
parent = cur
if (cur.val > val) {
cur = cur.left
} else {
cur = cur.right
}
}
let node = new TreeNode(val)
if (parent.val > val) {
parent.left = node
} else {
parent.right = node
}
}
return root
}

450.删除二叉搜索树中的节点 🌟🌟#

力扣链接 🌟🌟

题目描述#

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

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点; 如果找到了,删除它。 说明: 要求算法时间复杂度为 O(h)O(h),h 为树的高度。

示例:

binary-tree-node-delete
binary-tree-node-delete

解题思路#

删除节点情况:

  1. 没找到删除节点,则返回 null
  2. 删除节点是叶子节点,直接删除,不影响其他节点
  3. 删除节点只有一个子节点,删除节点,并用子节点替换删除节点
  4. 删除节点有两个子节点,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上(右子树中最小值),返回删除节点右孩子为新的根节点

递归#

递归三部曲:

  1. 确定递归函数的参数和返回值

    • 参数 1:根节点
    • 参数 2:要删除的值
    • 返回值:搜索的值所在的节点
  2. 确定终止条件

    如果 root 为 null 则说明没找到删除的节点,返回 root

    if (!root) return root
  3. 确定单层递归逻辑

    • 要删除的值大于 root.val,则继续递归右子树
    • 要删除的值小于 root.val,则继续递归左子树
    • 要删除的值就是当前节点时:
      • 节点是叶子结点
      • 节点只有一个子节点,则返回
      • 左右节点都存在,将将当前节点的左子树头节点指向右子树最左边节点,并将右子树指向删除节点的父节点
function deleteNode(root, key) {
if (!root) return root
if (root.val > key) {
root.left = deleteNode(root.left, key)
} else if (root.val < key) {
root.right = deleteNode(root.right, key)
} else {
if (!root.left && !root.right) return null
if (!root.left && root.right) return root.right
if (!root.right && root.left) return root.left
const rightNode = root.right
const minNode = getMinNode(root.right)
root.val = minNode.val
root.right = deleteNode(rightNode, minNode.val)
}
return root
}
function getMinNode(node) {
while (node.left) node = node.left;
return node;
}

迭代#

function deleteNodeIterative(root, key) {
let parent = null;
let current = root;
// 查找目标节点
while (current && current.val !== key) {
parent = current;
current = key < current.val ? current.left : current.right;
}
if (!current) return root; // 未找到
// Case 1: 无子节点
if (!current.left && !current.right) {
if (!parent) return null; // 删除根节点
parent[current === parent.left ? 'left' : 'right'] = null;
return root;
}
// Case 2: 单个子节点
if (!current.left || !current.right) {
const child = current.left || current.right;
if (!parent) return child; // 删除根节点
parent[current === parent.left ? 'left' : 'right'] = child;
return root;
}
// Case 3: 两个子节点
const successorParent = current;
let successor = current.right;
while (successor.left) {
successorParent = successor;
successor = successor.left;
}
current.val = successor.val;
if (successorParent === current) {
successorParent.right = successor.right;
} else {
successorParent.left = successor.right;
}
return root;
}
跟着卡哥学算法Day 20:二叉树常见题目7
https://wangxiang.website/posts/算法/binary-tree-code6/
作者
翔子
发布于
2025-03-03
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
翔子
前端开发工程师
公告
博客已从 VitePress 迁移到 Astro + Firefly 主题,223 篇文章全部保留。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
221
分类
9
标签
28
总字数
411,914
运行时长
0
最后活动
0 天前

文章目录