算法练习Day18 (Leetcode/Python-二叉树)

236. Lowest Common Ancestor of a Binary Tree

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”

Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
Output: 3
Explanation: The LCA of nodes 5 and 1 is 3.

题目中给了限制条件,每个node都是独一无二的且p!=q。

思路:

自下到上对树进行查找,后序遍历就是这样天然的回溯过程。

如何判断一个节点是否是p和q的公共祖先呢?如果找到一个节点,发现p在其左子树,q在其右子树或者反之,该节点即是公共祖先。注意,有可能某节点并没有左或者右子树了,即该节点自己既是p或者q,又是待求的公共祖先。

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if root == q or root == p or root is None:
            return root 

        # return value 
        left = self.lowestCommonAncestor(root.left, p, q) # left
        right = self.lowestCommonAncestor(root.right, p, q) # right

        if left is not None and right is not None: # middle
            return root 
        if left is None and right is not None:
            return right 
        elif left is not None and right is None:
            return left 
        else:
            return None 

701. Insert into a Binary Search Tree

You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST.

Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them.

思路:这道题仅仅是要求以合理方式把一个值插入二叉树搜索树中,由于二叉搜索树是有排序的(右子树值>父节点值>左子树值),所以只要把这个值插入到叶节点下就是合理的,这样原有的tree的结构几乎不用调整。那关键就在于如何找到这个叶节点。这里用preorder前序遍历。

  1. 递归函数 traversal:

    • 这个函数接受当前节点 cur 和要插入的值 val 作为参数。
    • 如果当前节点为空(cur is None),意味着已经到达了应该插入新节点的位置。这里创建一个新节点,并根据值的大小决定是连接到父节点的左侧还是右侧。
    • 如果当前节点不为空,函数会根据 val 和当前节点值 cur.val 的比较结果决定是向左子树递归还是向右子树递归。这是前序遍历的特征,因为操作是在递归调用前进行的。
  2. 前序遍历(Preorder Traversal):

    • 前序遍历的顺序是 根 -> 左 -> 右
    • 处理根节点(即决定在哪里插入新节点)发生在递归调用之前,符合前序遍历的特点。
  3. 函数 insertIntoBST:

    • 这个函数检查根节点是否为空,如果是,则直接在根位置创建新节点。
    • 如果根节点不为空,则调用 traversal 函数来找到正确的插入位置。
class Solution(object):
    def __init__(self):
        self.parent = None
    
    def traversal(self, cur, val):
        if cur is None:
            node = TreeNode(val)
            if val > self.parent.val:
                self.parent.right = node 
            else:
                self.parent.left = node
            return
        
        self.parent = cur
        if cur.val > val: # This is a binary search tree!! (nodes are sorted). 
            self.traversal(cur.left, val)
        #if cur.val < val:
        else:
            self.traversal(cur.right, val)


    def insertIntoBST(self, root, val):
        """
        :type root: TreeNode
        :type val: int
        :rtype: TreeNode
        """
        if root is None:
            root = TreeNode(val)
        else:
            self.traversal(root,val)
        return root 

以及迭代法:这题似乎迭代法更容易理解一些? 

class Solution:
    def insertIntoBST(self, root, val):
        if root is None:  # 如果根节点为空,创建新节点作为根节点并返回
            node = TreeNode(val)
            return node

        cur = root
        parent = root  # 记录上一个节点,用于连接新节点
        while cur is not None:
            parent = cur
            if cur.val > val:
                cur = cur.left
            else:
                cur = cur.right

        node = TreeNode(val)
        if val < parent.val:
            parent.left = node  # 将新节点连接到父节点的左子树
        else:
            parent.right = node  # 将新节点连接到父节点的右子树

        return root

450. Delete Node in a BST

Given a root node reference of a BST and a key, delete the node with the given key in the BST. Return the root node reference (possibly updated) of the BST.

Basically, the deletion can be divided into two stages:

  1. Search for a node to remove.
  2. If the node is found, delete the node.

思路:删除binary search tree的一个节点比加入一个节点要难。因为如果被删除的节点左右子树都存在,那么就需要考虑补位问题。这里讨论四种删除节点的可能性:(见以下解答的comments)

class Solution(object):
    def deleteNode(self, root, key):
        """
        :type root: TreeNode
        :type key: int
        :rtype: TreeNode
        """
        if root is None:
            return root
        if root.val == key:
            if root.left is None and root.right is None: # case1: 待删除的节点是叶节点,直接删除
                return None
            elif root.left is None: # case2: 无左子树,那直接右子树补位
                return root.right
            elif root.right is None: # case3: 无右子树,那直接左子树补位
                return root.left 
            else: # case4: 左右子树都存在,将左子树放到删除的节点的柚子树的最左边节点的左叶子上。返回右子树.
                cur = root.right 
                while cur.left is not None: 
                    cur = cur.left 
                cur.left = root.left 
                return root.right

        if root.val > key:
            root.left = self.deleteNode(root.left, key)
        if root.val < key:
            root.right = self.deleteNode(root.right, key)    
        return root   

递归法二:

class Solution:
    def deleteNode(self, root, key):
        if root is None:  # 如果根节点为空,直接返回
            return root
        if root.val == key:  # 找到要删除的节点
            if root.right is None:  # 如果右子树为空,直接返回左子树作为新的根节点
                return root.left
            cur = root.right
            while cur.left:  # 找到右子树中的最左节点
                cur = cur.left
            root.val, cur.val = cur.val, root.val  # 将要删除的节点值与最左节点值交换
        root.left = self.deleteNode(root.left, key)  # 在左子树中递归删除目标节点
        root.right = self.deleteNode(root.right, key)  # 在右子树中递归删除目标节点
        return root

迭代法:总感觉迭代法相对递归法更容易理解,尤其是对于BST。

class Solution:
    def deleteOneNode(self, target: TreeNode) -> TreeNode:
        """
        将目标节点(删除节点)的左子树放到目标节点的右子树的最左面节点的左孩子位置上
        并返回目标节点右孩子为新的根节点
        是动画里模拟的过程
        """
        if target is None:
            return target
        if target.right is None:
            return target.left
        cur = target.right
        while cur.left:
            cur = cur.left
        cur.left = target.left
        return target.right

    def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
        if root is None:
            return root
        cur = root
        pre = None  # 记录cur的父节点,用来删除cur
        while cur:
            if cur.val == key:
                break
            pre = cur
            if cur.val > key:
                cur = cur.left
            else:
                cur = cur.right
        if pre is None:  # 如果搜索树只有头结点
            return self.deleteOneNode(cur)
        # pre 要知道是删左孩子还是右孩子
        if pre.left and pre.left.val == key:
            pre.left = self.deleteOneNode(cur)
        if pre.right and pre.right.val == key:
            pre.right = self.deleteOneNode(cur)
        return root

相关推荐

  1. 算法练习Day18 (Leetcode/Python-

    2023-12-22 08:00:05       45 阅读
  2. 算法练习Day17 (Leetcode/Python-

    2023-12-22 08:00:05       50 阅读
  3. 算法训练营Day15()

    2023-12-22 08:00:05       45 阅读
  4. 算法训练营Day14()

    2023-12-22 08:00:05       49 阅读
  5. 算法训练营day19,8-2

    2023-12-22 08:00:05       31 阅读
  6. day 18(五)

    2023-12-22 08:00:05       35 阅读

最近更新

  1. 并发请求的艺术:Postman中实现高效API测试

    2023-12-22 08:00:05       0 阅读
  2. 关于TCP的三次握手流程

    2023-12-22 08:00:05       1 阅读
  3. stm32毫秒ms延时,HAL_Delay()

    2023-12-22 08:00:05       1 阅读
  4. nftables(4)表达式(2)主要表达式(PRIMARY EXPRESSIONS)

    2023-12-22 08:00:05       1 阅读
  5. C++八股(三)之虚函数

    2023-12-22 08:00:05       1 阅读

热门阅读

  1. python3+selenium 切换窗口方法

    2023-12-22 08:00:05       41 阅读
  2. 流媒体知识总结

    2023-12-22 08:00:05       43 阅读
  3. 在 Go 语言中使用 regexp 包处理正则表达式

    2023-12-22 08:00:05       34 阅读
  4. Ansible3

    Ansible3

    2023-12-22 08:00:05      40 阅读
  5. css学习笔记5

    2023-12-22 08:00:05       41 阅读
  6. Android 9.0 应用权限屏蔽位置信息

    2023-12-22 08:00:05       29 阅读
  7. MongoDB的面试题与答案

    2023-12-22 08:00:05       35 阅读
  8. GBASE南大通用集群负载均衡

    2023-12-22 08:00:05       33 阅读