今日内容:
● 235. 二叉搜索树的最近公共祖先
● 701.二叉搜索树中的插入操作
● 450.删除二叉搜索树中的节点
1. 二叉搜索树的最近公共祖先
关联 leetcode 235. 二叉搜索树的最近公共祖先
- 思路
- 当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[q, p]区间中,那么cur就是 q和p的最近公共祖先
- 此时 q p 一定处于左右孩子中,或者一个就是cur节点本身
- 不用使用回溯,二叉搜索树自带方向性,可以方便的从上向下查找目标区间,遇到目标区间内的节点,直接返回
- 当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[q, p]区间中,那么cur就是 q和p的最近公共祖先
- 题解
-
递归
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { rootVal := root.Val //当前节点值大了,往左子树搜索 if rootVal > q.Val && rootVal > p.Val { left := lowestCommonAncestor(root.Left, p, q) if left != nil { return left } } //当前节点值小了,往右子树搜索 if rootVal < q.Val && rootVal < p.Val { right := lowestCommonAncestor(root.Right, p, q) if right != nil { return right } } //当前节点刚好在p,q的节点值之间 return root }
-
迭代
func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { for root != nil { if root.Val > q.Val && root.Val > p.Val { //当前节点值大了, 往小了搜索 root = root.Left } else if root.Val < q.Val && root.Val < p.Val { //当前节点值小了, 往大了搜索 root = root.Right } else { //当前节点值刚好在取值范围内,直接返回 return root } } return nil }
-
2. 二叉搜索树中的插入操作
关联 leetcode 701.二叉搜索树中的插入操作
-
思路
- 题目条件
- 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
- 只要遍历二叉搜索树,找到空节点 插入元素就可以了,那么这道题其实就简单了。
- 题目条件
-
题解
- 个人解法
- 二叉搜索树是有序的
- 只用看插入值与当前的节点值得大小关系,插在哪边即可
- 二叉搜索树是有序的
func insertIntoBST(root *TreeNode, val int) *TreeNode { if root == nil {//当前是空树or叶子节点直接返回新增节点 return &TreeNode{Val: val} } if val < root.Val { if root.Left == nil { root.Left = &TreeNode{Val: val} return root } left := insertIntoBST(root.Left, val) root.Left = left } if val > root.Val { if root.Right == nil { root.Right = &TreeNode{Val: val} return root } right := insertIntoBST(root.Right, val) root.Right = right } return root }
- 个人解法
-
递归
func insertIntoBST(root *TreeNode, val int) *TreeNode { if root == nil { return &TreeNode{ Val: val, } } if root.Val > val { root.Left = insertIntoBST(root.Left, val) } else { root.Right = insertIntoBST(root.Right, val) } return root }
-
迭代
func insertIntoBST(root *TreeNode, val int) *TreeNode { if root == nil { return &TreeNode{Val:val} } node := root//当前操作节点 var pnode *TreeNode//前一个父亲节点,后面追加节点标志 for node != nil { if val > node.Val { pnode = node node = node.Right } else { pnode = node node = node.Left } } if val > pnode.Val { pnode.Right = &TreeNode{Val: val} } else { pnode.Left = &TreeNode{Val: val} } return root }
3. 删除二叉搜索树中的节点
关联 leetcode 450.删除二叉搜索树中的节点
相对于 插入操作,本题就有难度了,涉及到改树的结构
题目链接/文章讲解:https://programmercarl.com/0450.删除二叉搜索树中的节点.html
视频讲解:调整二叉树的结构最难!| LeetCode:450.删除二叉搜索树中的节点_哔哩哔哩_bilibili
-
思路
- 删除可能导致二叉搜索树结构改变
- 区分删除节点位置
- 没找到节点:
- 不做任何处理
- 叶子节点:
- 直接删就好
- 左不空,右空
- 直接把它父节点指向它左孩子
- 左空,右不空
- 直接把它父节点指向它右孩子
- 左不空,右不空
- 任意选取一个孩子去继位
- 右孩子继位
- 把它父节点指向右孩子
- 原左孩子,指向右孩子的左子树的最左节点
- 左孩子继位
- 把它父节点指向右孩子
- 原右孩子,指向左孩子的右子树的最右节点
- 右孩子继位
- 任意选取一个孩子去继位
- 没找到节点:
- 区分删除节点位置
- 删除可能导致二叉搜索树结构改变
-
题解
- 递归
func deleteNode(root *TreeNode, key int) *TreeNode { if root == nil { //没找到匹配值 return root } //当前节点就是值 if root.Val == key { //1. leaf node if root.Left == nil && root.Right == nil { return nil } //2. 左不空,右空 if root.Left != nil && root.Right == nil { return root.Left } //2. 左空,右不空 if root.Left == nil && root.Right != nil { return root.Right } // 选左孩子 cur := root.Left pre := cur for cur != nil { pre = cur cur = cur.Right } pre.Right = root.Right return root.Left } if root.Val > key {//节点值大了,去左子树删,更新左子树 root.Left = deleteNode(root.Left, key) } else {//节点值小了,去右子树删,更新右子树 root.Right = deleteNode(root.Right, key) } return root }
- 迭代
func deleteOneNode(target *TreeNode) *TreeNode { if target == nil { return target } if target.Right == nil { return target.Left } cur := target.Right for cur.Left != nil { cur = cur.Left } cur.Left = target.Left return target.Right } func deleteNode(root *TreeNode, key int) *TreeNode { // 特殊情况处理 if root == nil { return root } cur := root var pre *TreeNode for cur != nil { if cur.Val == key { break } pre = cur if cur.Val > key { cur = cur.Left } else { cur = cur.Right } } if pre == nil { return deleteOneNode(cur) } // pre 要知道是删除左孩子还有右孩子 if pre.Left != nil && pre.Left.Val == key { pre.Left = deleteOneNode(cur) } if pre.Right != nil && pre.Right.Val == key { pre.Right = deleteOneNode(cur) } return root }
9. 题外话
- 因为二叉搜索树添加节点只需要在叶子上添加就可以的,不涉及到结构的调整
- 删除节点操作涉及到结构的调整。
- 充分考虑删除节点所在位置