代码随想录算法训练营第十四天 | 二叉树系列5

二叉树系列5

  • 235 二叉搜索树的最近公共祖先
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 701 二叉搜索树中的插入操作
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 450 删除二叉搜索树中的节点
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 669 修剪二叉搜索树
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 108 将有序数组转换为二叉搜索树
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 538 把二叉搜索树转换为累加树
    • 重点
    • 代码随想录的代码
    • 我的代码(当天晚上理解后自己编写)
  • 二叉树总结篇!

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

重点

利用二叉搜索树的特性,不再需要后序遍历,因为不再需要回溯,利用二叉搜索树的特性,从上至下,前序遍历即可。

一旦遇到了一个节点,其值在 p 和 q 之间,那么这个节点一定是最近公共祖先,因为 p 在其左 , q 在其右,再往下走一步,不管是向左还是向右,一定会错过 p 或 q 中的一个。

理解清楚这点很重要!

代码随想录的代码

递归法(版本一)

class Solution:
    def traversal(self, cur, p, q):
        if cur is None:
            return cur
                                                        # 中
        if cur.val > p.val and cur.val > q.val:           # 左
            left = self.traversal(cur.left, p, q)
            if left is not None:
                return left

        if cur.val < p.val and cur.val < q.val:           # 右
            right = self.traversal(cur.right, p, q)
            if right is not None:
                return right

        return cur

    def lowestCommonAncestor(self, root, p, q):
        return self.traversal(root, p, q)

迭代法(版本二)精简

class Solution:
    def lowestCommonAncestor(self, root, p, q):
        if root.val > p.val and root.val > q.val:
            return self.lowestCommonAncestor(root.left, p, q)
        elif root.val < p.val and root.val < q.val:
            return self.lowestCommonAncestor(root.right, p, q)
        else:
            return root


迭代法

class Solution:
    def lowestCommonAncestor(self, root, p, q):
        while root:
            if root.val > p.val and root.val > q.val:
                root = root.left
            elif root.val < p.val and root.val < q.val:
                root = root.right
            else:
                return root
        return None

我的代码(当天晚上理解后自己编写)

不知道写的好不好?和版本一在逻辑上差不多。

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root == None:
            return None

        if root.val == p.val or root.val == q.val :
            return root

        bool_p = root.val > p.val
        bool_q = root.val > q.val

        if bool_p and bool_q :
            return self.lowestCommonAncestor(root.left,p,q)

        elif not bool_p and not bool_q :
            return self.lowestCommonAncestor(root.right,p,q)

        else :
            return root

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

重点

这道题目其实是一道简单题目,但是题目中的提示:有多种有效的插入方式,还可以重构二叉搜索树,一下子吓退了不少人,瞬间感觉题目复杂了很多。

其实可以不考虑题目中提示所说的改变树的结构的插入方式。

其实:只要按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了。

就直接往叶子节点插入,不考虑别的。

不要把题目想复杂了,这就是一颗二叉搜索树而已,又不是平衡二叉树,要调整左右子树的高度等。

代码随想录的代码

递归法(版本一)

class Solution:
    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:
            self.traversal(cur.left, val)
        if cur.val < val:
            self.traversal(cur.right, val)

    def insertIntoBST(self, root, val):
        self.parent = TreeNode(0)
        if root is None:
            return TreeNode(val)
        self.traversal(root, val)
        return root

递归法(版本二)

class Solution:
    def insertIntoBST(self, root, val):
        if root is None:
            return TreeNode(val)
        parent = None
        cur = root
        while cur:
            parent = cur
            if val < cur.val:
                cur = cur.left
            else:
                cur = cur.right
        if val < parent.val:
            parent.left = TreeNode(val)
        else:
            parent.right = TreeNode(val)
        return root

递归法(版本三)

class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if root is None or root.val == val:
            return TreeNode(val)
        elif root.val > val:
            if root.left is None:
                root.left = TreeNode(val)
            else:
                self.insertIntoBST(root.left, val)
        elif root.val < val:
            if root.right is None:
                root.right = TreeNode(val)
            else:
                self.insertIntoBST(root.right, val)
        return root

递归法(版本四)

class Solution:
    def insertIntoBST(self, root, val):
        if root is None:
            node = TreeNode(val)
            return node

        if root.val > val:
            root.left = self.insertIntoBST(root.left, val)
        if root.val < val:
            root.right = self.insertIntoBST(root.right, 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

我的代码(当天晚上理解后自己编写)

class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
        if root == None :
            return TreeNode(val)
        self.digui(root,val)
        return root

    def digui(self,root,val):
        if root == None :
            node = TreeNode(val)
            return node
        
        if root.val > val :
            left = self.digui(root.left,val)
            if left != None :
                root.left = left

        if root.val < val :
            right = self.digui(root.right,val)

            if right != None :
                root.right = right

如果理解了递归解法的版本四,那么上述的各种判断就不用写那么多了,如果Left不做更改,那么root.left更新的时候,就是原树的root.left。

class Solution:
    def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
       
        if root == None :
            node = TreeNode(val)
            return node
        
        if root.val > val :
            left = self.insertIntoBST(root.left,val)
            
            root.left = left

        if root.val < val :
            right = self.insertIntoBST(root.right,val)

            
            root.right = right

        return root

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

思路卡在了当所要删除的节点的左右子树均不为空时,如何操作?

第一遍思考,思路方向是旋转左右子树,但这是不对的。

左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

重点

代码随想录的代码

递归法(版本一)

class Solution:
    def deleteNode(self, root, key):
        if root is None:
            return root
        if root.val == key:
            if root.left is None and root.right is None:
                return None
            elif root.left is None:
                return root.right
            elif root.right is None:
                return root.left
            else:
                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

迭代法

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

我的代码(当天晚上理解后自己编写)

思路上,意识到了,要找到所要删除节点的右子树的最左节点,然后将要删除节点的左子树嫁接上去。

但是认为需要一个parent全局变量来保存当前遍历节点的右节点,但是其实是不必的。

只需要一个root,返回也只返回删除root后的根节点就好。

判断条件是学习的关键。

class Solution:
    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        if root == None :
            return None
        if root.val == key :
            if root.left == None and root.right == None :
                return None
            elif root.left == None and root.right != None :
                return root.right
            elif root.left != None and root.right == None :
                return root.left

            else :
                res = root.right
                while res.left :
                    res = res.left
                res.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

669 修剪二叉搜索树

这里的右子树返回给父节点的左孩子的思想,在第一次看题时想到了一点。

重点

该题中存在一种很容易陷入误区的错误写法,注意即可。

代码随想录的代码

递归法(版本一)

class Solution:
    def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
        if root is None:
            return None
        if root.val < low:
            # 寻找符合区间 [low, high] 的节点
            return self.trimBST(root.right, low, high)
        if root.val > high:
            # 寻找符合区间 [low, high] 的节点
            return self.trimBST(root.left, low, high)
        root.left = self.trimBST(root.left, low, high)  # root.left 接入符合条件的左孩子
        root.right = self.trimBST(root.right, low, high)  # root.right 接入符合条件的右孩子
        return root

递归法(版本二)精简

class Solution:
    def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
        if root is None:
            return None
        if root.val < low:
            return self.trimBST(root.right, low, high)
        if root.val > high:
            return self.trimBST(root.left, low, high)
        root.left = self.trimBST(root.left, low, high)
        root.right = self.trimBST(root.right, low, high)
        return root

迭代法

class Solution:
    def trimBST(self, root: TreeNode, L: int, R: int) -> TreeNode:
        if not root:
            return None
        
        # 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭
        while root and (root.val < L or root.val > R):
            if root.val < L:
                root = root.right  # 小于L往右走
            else:
                root = root.left  # 大于R往左走
        
        cur = root
        
        # 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况
        while cur:
            while cur.left and cur.left.val < L:
                cur.left = cur.left.right
            cur = cur.left
        
        cur = root
        
        # 此时root已经在[L, R] 范围内,处理右孩子大于R的情况
        while cur:
            while cur.right and cur.right.val > R:
                cur.right = cur.right.left
            cur = cur.right
        
        return root

我的代码(当天晚上理解后自己编写)

这题的代码逻辑还是要好好理顺的,看着简单,但是前面两个 if 的判断,如果root是要删除的节点,如果 root 的值小,返回了当前 root 的右子树,而在后面递归逻辑中,root 的父节点的左孩子,会接住 root 的右子树,而如果 root 是小值,那么root的左孩子所有也和 root 一起被删除了。

class Solution:
    def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:
        if root is None:
            return None
        if root.val < low:
            
            return self.trimBST(root.right, low, high)
        if root.val > high:
            
            return self.trimBST(root.left, low, high)
            
        root.left = self.trimBST(root.left, low, high)  
        root.right = self.trimBST(root.right, low, high)  
        return root

108 将有序数组转换为二叉搜索树

找中间节点,然后拆分区间就可以了吧。

这是第一次看题想到的思路。

重点

代码随想录的代码

递归法

class Solution:
    def traversal(self, nums: List[int], left: int, right: int) -> TreeNode:
        if left > right:
            return None
        
        mid = left + (right - left) // 2
        root = TreeNode(nums[mid])
        root.left = self.traversal(nums, left, mid - 1)
        root.right = self.traversal(nums, mid + 1, right)
        return root
    
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        root = self.traversal(nums, 0, len(nums) - 1)
        return root

迭代法

from collections import deque

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if len(nums) == 0:
            return None
        
        root = TreeNode(0)  # 初始根节点
        nodeQue = deque()   # 放遍历的节点
        leftQue = deque()   # 保存左区间下标
        rightQue = deque()  # 保存右区间下标
        
        nodeQue.append(root)               # 根节点入队列
        leftQue.append(0)                  # 0为左区间下标初始位置
        rightQue.append(len(nums) - 1)     # len(nums) - 1为右区间下标初始位置

        while nodeQue:
            curNode = nodeQue.popleft()
            left = leftQue.popleft()
            right = rightQue.popleft()
            mid = left + (right - left) // 2

            curNode.val = nums[mid]  # 将mid对应的元素给中间节点

            if left <= mid - 1:  # 处理左区间
                curNode.left = TreeNode(0)
                nodeQue.append(curNode.left)
                leftQue.append(left)
                rightQue.append(mid - 1)

            if right >= mid + 1:  # 处理右区间
                curNode.right = TreeNode(0)
                nodeQue.append(curNode.right)
                leftQue.append(mid + 1)
                rightQue.append(right)

        return root

我的代码(当天晚上理解后自己编写)

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
        if nums == [] :
            return None
        n = len(nums)
        idx = n//2
        middle = nums[idx]
        mroot = TreeNode(middle)
        left = nums[:idx]
        right = nums[idx+1:n]

        lnode = self.sortedArrayToBST(left)

        rnode = self.sortedArrayToBST(right)

        mroot.left = lnode
        mroot.right = rnode

        return mroot

538 把二叉搜索树转换为累加树

何为,累加树?

不明白概念,所以第一次审题,并无思路。

没仔细审题!累加树不是一个标准定义,是题目中自己定义的:使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

换成数组中,就是一个倒序遍历,然后一直累加!

既然如此,逻辑就是反中序遍历。同样要搞一个 pre 全局变量,记录一直以来的累计值。

想明白这个逻辑之后,递归很简单。

重点

代码随想录的代码

递归法(版本一)

class Solution:
    def convertBST(self, root: TreeNode) -> TreeNode:
        self.pre = 0  # 记录前一个节点的数值
        self.traversal(root)
        return root
    def traversal(self, cur):
        if cur is None:
            return        
        self.traversal(cur.right)
        cur.val += self.pre
        self.pre = cur.val
        self.traversal(cur.left)
    

递归法(版本二)

class Solution:
    def __init__(self):
        self.count = 0

    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root == None:
            return 
        '''
        倒序累加替换:  
        '''
        # 右
        self.convertBST(root.right)

        # 中
        # 中节点:用当前root的值加上pre的值
        self.count += root.val

        root.val = self.count 

        # 左
        self.convertBST(root.left)

        return root 

迭代法(版本一)

class Solution:
    def __init__(self):
        self.pre = 0  # 记录前一个节点的数值
    
    def traversal(self, root):
        stack = []
        cur = root
        while cur or stack:
            if cur:
                stack.append(cur)
                cur = cur.right  # 右
            else:
                cur = stack.pop()  # 中
                cur.val += self.pre
                self.pre = cur.val
                cur = cur.left  # 左
    
    def convertBST(self, root):
        self.pre = 0
        self.traversal(root)
        return root

迭代法(版本二)

class Solution:
    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root: return root
        stack = []
        result = []
        cur = root
        pre = 0
        while cur or stack:
            if cur:
                stack.append(cur)
                cur = cur.right
            else: 
                cur = stack.pop()
                cur.val+= pre
                pre = cur.val
                cur =cur.left
        return root

我的代码(当天晚上理解后自己编写)

class Solution:
    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        self.pre = 0
        return self.digui(root)

    def digui(self,root):
        if root == None :
            return 

        self.digui(root.right)



        root.val += self.pre
        self.pre = root.val

        self.digui(root.left)

        return root

二叉树总结篇!

牛逼!代码随想录这篇总结写的真好,N刷时,就对着这一篇刷就没问题。

二叉树总结篇

你可能感兴趣的:(算法,python,开发语言)