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

2023-12-21 18:16:59

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

文章来源:https://blog.csdn.net/m0_54919454/article/details/135127441
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。