Day 22 二叉树:235. BST最近公共祖先, 701. BST插入, 450. BST删除

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

  • 思路
    • example
    • 利用二叉搜索树性质(排序)
    • 注意条件:
      • 所有节点的值都是唯一的。
      • p、q 为不同节点且均存在于给定的二叉搜索树中。
    • 递归,自上而下 (更接近前序遍历)
    • 四种情况:
      • 当前节点val 等于p,q其中一个, return 当前节点
      • 当前节点val 在q,q对应值之间, return 当前节点
      • 当前节点val > p,q值,在左子树搜索 (递归)
      • 当前节点val < p,q值,在右子树搜索 (递归)
  • 复杂度. 时间:O(n), 空间: O(n)
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if p.val > q.val:
            p, q = q, p
        # p.val < q.val
        if root.val > q.val: 
            return self.lowestCommonAncestor(root.left, p, q)
        if root.val < p.val:
            return self.lowestCommonAncestor(root.right, p, q)
        # otherwise 
        return root
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        def traversal(root):
            if root.val == p.val or root.val == q.val:
                return root 
            if p.val < root.val < q.val or q.val < root.val < p.val:
                return root 
            if p.val < root.val and q.val < root.val:
                return traversal(root.left) 
            if p.val > root.val and q.val > root.val:
                return traversal(root.right)
        return traversal(root)
  • 迭代, 排序结构适合用迭代
    • 遍历cur,每次通过比大小来决定方向(cur = cur.left 或者 cur = cur.right)
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        cur = root  
        while cur:
            if cur.val > p.val and cur.val > q.val:
                cur = cur.left 
            elif cur.val < p.val and cur.val < q.val:
                cur = cur.right  
            else:
                break 
        return cur  
class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if q.val < p.val:
            p, q = q, p 
        cur = root 
        while cur:
            if cur == p or cur == q:
                return cur 
            if cur.val < p.val:
                cur = cur.right 
            elif cur.val > q.val:
                cur = cur.left 
            else:
                return cur 

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

  • 思路
    • example
    • 搜索遇空节点即是插入位置。
    • 递归 (无返回值)
      • 处理空节点
      • 维护parent节点(方便插入操作)
  • 复杂度. 时间:O(n), 空间: O(n)
class Solution:
    def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
        def traversal(root):
            nonlocal parent 
            if root == None: 
                node = TreeNode(val)
                if parent.val < val: 
                    parent.right = node  
                else: # >  
                    parent.left = node  
                return 
            parent = root 
            if root.val < val:
                traversal(root.right)
            else:
                traversal(root.left)
            return 
        if root == None:
            return TreeNode(val)
        parent = None  
        traversal(root)
        return root 
  • 递归,后序,有返回值
class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        def traversal(root):
            if root == None:
                return TreeNode(val)
            if root.val > val:
                root.left = traversal(root.left)
            if root.val < val:
                root.right = traversal(root.right) 
            return root 
        return traversal(root) 
class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if root == None:
            return TreeNode(val, None, None)  
        if root.val > val:
            root.left = self.insertIntoBST(root.left, val) 
        else:
            root.right = self.insertIntoBST(root.right, val)
        return root  
  • 最自然的是迭代
    • 记录parent节点
class Solution:
    def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
        cur = root 
        parent, child_type = None, 'left' 
        while cur:
            parent = cur 
            if cur.val > val:
                cur = cur.left 
                child_type = 'left'
            else: 
                cur = cur.right 
                child_type = 'right'
        node = TreeNode(val) 
        if parent == None: 
            return node  
        if child_type == 'left':
            parent.left = node  
        else:
            parent.right = node  
        return root 
class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if root == None:
            return TreeNode(val) 
        cur = root 
        while cur:
            if cur.val < val:
                if cur.right:
                    cur = cur.right 
                else:
                    cur.right = TreeNode(val)
                    return root 
            elif cur.val > val:
                if cur.left:
                    cur = cur.left 
                else:
                    cur.left = TreeNode(val)
                    return root  

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

  • 思路
    • example
    • 注意树中可能不包含需要删除的节点
    • 递归,返回值:以当前节点为根节点并且已删除所要求节点的树。
      • 当前节点为删除节点
        • 左子树加到 右子树最小值 的左边
        • 或者把右子树最小值对应node移动到删除位置
        • 。。。
      • 所删除节点在左子树, 递归
      • 所删除节点在右子树,递归
  • 复杂度. 时间:O(h), h为树的高度。
class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        if root == None:
            return None
        if root.val == key:
            if root.right == None:
                return root.left
            root_new = root.right 
            cur = root.right
            while cur.left:
                cur = cur.left
            cur.left = root.left 
            root = root_new
        elif root.val > key:
            root.left = self.deleteNode(root.left, key)
        #if root.val < key:
        else: 
            root.right = self.deleteNode(root.right, key)
        return root 
class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        def traversal(root):
            if root == None:
                return None 
            if root.val > key:
                root.left = traversal(root.left) 
            elif root.val < key:
                root.right = traversal(root.right) 
            else:
                if root.right == None:
                    return root.left 
                cur = root.right 
                while cur.left:
                    cur = cur.left 
                cur.left = root.left 
                root = root.right  
            return root 
        return traversal(root) 
class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        if root == None:
            return root    
        if root.val > key:
            root.left = self.deleteNode(root.left, key) 
            return root  
        elif root.val < key:
            root.right = self.deleteNode(root.right, key)
            return root  
        else:
            righthead = root.right  
            if righthead == None:
                return root.left   
            while righthead.left:
                righthead = righthead.left   
            righthead.left = root.left  
            return root.right   # !!!, not return righthead  
  • 迭代
    • 需要人工维护parent信息
    • 手动“删除”节点并完成新旧节点的链接
      • 递归方法因为有额外的栈存储,不需要维护parent。并且链接可以简单递归更新root.left, root.right实现。
    • 注意顺序,避免进入细节的讨论。比如:删除节点不在树中,删除节点在root,删除节点是叶子节点,。。。
    • 找到欲删除节点后
      • 第1步:把左子树merge到右子树,保存右子树的头结点subtree(注意右子树可能为空)
      • 第2步:根据parent.val与Key的值,链接parent与subtree.
class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        if root == None:
            return None
        dummyhead = TreeNode(float('inf'), root, None)
        cur, parent = dummyhead, dummyhead 
        while cur:
            if cur.val == key:
                break 
            elif cur.val > key: 
                parent = cur 
                cur = cur.left 
            else: 
                parent = cur
                cur = cur.right  
        # now cur is the node that needs to be deleted 
        if cur == None: # key is not on the tree
            return dummyhead.left
        #1 merge left-subtree and right-subtree, get the subroot for the new right-subtree
        if cur.right:
            subroot = cur.right # 
            node = cur.right
            while node.left:
                node = node.left 
            # link left-subtree to the left of the min of right-subtree
            node.left = cur.left 
        else:
            subroot = cur.left
        #2 link parent and subtree
        if parent.val > key: # parent.left = key
            parent.left = subroot 
        else:
            parent.right = subroot 
        return dummyhead.left  
  • 或者这样: 尽量保持原有的结构


class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        if root == None:
            return None 
        if root.val == key:
            if root.right == None:
                return root.left 
            # 找出右子树的最小节点, 删除,然后重新"赋值"当前root
            cur = root.right 
            while cur.left:
                cur = cur.left 
            root.right = self.deleteNode(root.right, cur.val)
            cur.left = root.left 
            cur.right = root.right 
            root = cur 
        elif root.val > key:
            root.left = self.deleteNode(root.left, key) 
        else: # root.val < key
            root.right = self.deleteNode(root.right, key) 
        return root 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容