代码随想录Day15:二叉树Part2

Leetcode 102. 二叉树的层序遍历

讲解前:

今天对于二叉树的层序遍历竟然说了可以直接刷十道题让我吓了一跳,我会看情况试试能不能刷完,我之前做过二查收的层序遍历的,对于利用队列的思维还有思路是记得的,我先试一试能不能完成代码

ok还好我还能记得怎么写这个层序遍历,虽然因为给level赋值的地方放错了位置导致debug了一段时间

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        res = []
        q = deque()
        q.append(root)

        while q:
            level = []
            for _ in range(len(q)):
                node = q.popleft()
                if node:
                    level.append(node.val)
                    q.append(node.left)
                    q.append(node.right)
        
            if level:
                res.append(level)
        
        return res

其实就是在每一层的时候,q中刚好存放的就是这一层需要check的node的数量,有可能里面有none但是没关系,然后我们直接pop掉所有,然后如果不是none就加入到当前层的list里面,并且再把他的left 和 right加到queue中去,这里因为for 循环一开始就订好了循环的次数是没有更新的queue也就是上一层存放的node数量,所以不用担心

讲解后:

看完了卡哥的讲解之后大体思路和我是一样的,只是卡哥在添加节点的左右子节点时确认了他们是否为空再添加,这样就不用在遍历每一层的q的值的时候检查是否node了,这样也可以减少一些时间因为我们通过多余的两个判断减少了每一层for loop循环的次数

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        res = []
        if not root:
            return res

        q = deque()
        q.append(root)

        while q:
            level = []
            for _ in range(len(q)):
                node = q.popleft()
                level.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
        
            if level:
                res.append(level)
        
        return res

Leetcode 107. 二叉树层序遍历II

这里我们继续去做一写利用层序遍历的题,这道题我看了一会题解发现要么就是和上一题一样最后直接翻转一下res就可以了,要么就是储存每一个level 到stack里面然后按pop的顺序返回,我觉得实在没什么必要,python的list本来就能当stack

class Solution:
    def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        res = []
        if not root:
            return res
        q = deque()
        q.append(root)

        while q:
            level = []
            for _ in range(len(q)):
                node = q.popleft()
                level.append(node.val)
                if node.left: q.append(node.left)
                if node.right: q.append(node.right)

            res.append(level)

        return res[::-1]

Leetcode 199. 二叉树的右视图

这道题我拿到题的一瞬间还在纳闷为什么需要从层序遍历来做,用dfs然后一直搜索右节点不就好了吗,但是我写出来然后虽然ac了但是没通过我才发现,这道题说的又不是把所有的最右边的右节点返回,是要从右看的视图,也就是说当树中没有右节点的时候,可以直接看到左节点,那么左节点就也要算上

然后呢我就回归了利用层序遍历的想法,这样以来的话就很简单了,只需要每一次添加到res中的数字并不是整个层的数字,而是层中数字最右边的那一个就行了,也就是level[-1]

class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        if not root:
            return res
        
        q = deque()
        q.append(root)

        while q:
            level = []
            for _ in range(len(q)):
                node = q.popleft()
                level.append(node.val)

                if node.left: q.append(node.left)
                if node.right: q.append(node.right)
            
            res.append(level[-1])
        
        return res

Leetcode 637. 二叉树的层平均值

同样的思路,还是正常把每个level的数字都放进list,最后只需要把当前level的平均值加到res里面就可以了

class Solution:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        res = []
        if not root:
            return res
        
        q = deque()
        q.append(root)

        while q:
            level = []
            for _ in range(len(q)):
                node = q.popleft()
                level.append(node.val)
                if node.left: q.append(node.left)
                if node.right: q.append(node.right)
            
            res.append(sum(level) / len(level))
        
        return res

 Leetcode 429. N叉树的层序遍历

这道题依然没什么区别,只是当我们pop掉层的node之后,当我们需要把他们的子节点继续往queue里面加入的时候,不在是只有left 和right了,而是直接遍历所有的children

class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        res = []
        if not root:
            return res
        
        q = deque()
        q.append(root)

        while q:
            level = []
            for _ in range(len(q)):
                node = q.popleft()
                level.append(node.val)
                for n in node.children:
                    q.append(n)
            
            res.append(level)
        
        return res

Leetcode 515. 在每个树行中找最大值

这道题也一样,不过我们没有必要再用一个额外的空间来保存level了,我们只需要每一行的最大值,所以一开始就initialize一个max到负无穷小,然后每一次检查一个node.val 就和max作比较,最后直接把max append到res就行了

class Solution:
    def largestValues(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        if not root:
            return res
        
        q = deque()
        q.append(root)
        
        while q:
            max_ = float('-inf')
            for _ in range(len(q)):
                node = q.popleft()
                max_ = max(max_, node.val)
                if node.left: q.append(node.left)
                if node.right: q.append(node.right)
            
            res.append(max_)
        
        return res

Leetcode 116. 填充每个节点下一个右侧节点指针/II

这道题有一点点奇怪一开始看上去,但是后来发现其实并不难,利用层序遍历非常方便,需要注意的点是这样的,这里对于每一层的node的next指针,都是从左到右指向自己的右边,唯独最右边的node的next就需要指向none,所有如果我们依然遍历每一层的node从左到右,我们还需要特别处理最后一个node,就很麻烦,如果我们一开始就从右边开始,然后新建一个指针curr,初始为none,那么就可以每次单纯的让node.next 指向curr就可以了,把curr更新成刚处理过的node

class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        if not root:
            return None

        q = deque()
        q.append(root)

        while q:
            curr = None
            for _ in range(len(q)):
                node = q.popleft()
                node.next = curr
                curr = node

                if node.right: q.append(node.right)
                if node.left: q.append(node.left)
            
        return root

Leetcode 104. 二叉树的最大深度

这道题之前只知道可以用dfs来递归做,但是今天做完广度优先搜索之后发现广度优先的思路也非常简单,只需要每一次我们遍历到新的一层的时候把深度+1就可以了

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        res = 0
        if not root:
            return res

        q = deque()
        q.append(root)

        while q:
            res = res + 1
            for _ in range(len(q)):
                node = q.popleft()
                if node.left: q.append(node.left)
                if node.right: q.append(node.right)
            
        return res

Leetcode 111. 二叉树的最小深度

这道题继续用广度优先搜索的话也可以,因为我们是一层一层搜索的,所以但凡我们遇到了一层中包含了一个leaf node,那就直接return 当前的层数就可以了

class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        res = 0
        if not root:
            return res
        
        q = deque()
        q.append(root)

        while q:
            res = res + 1
            for _ in range(len(q)):
                node = q.popleft()
                if not node.left and not node.right:
                    return res
                else:
                    if node.left: q.append(node.left)
                    if node.right: q.append(node.right)

Leetcode 226. 翻转二叉树

讲解前:

这道题之前就做过,其实就是靠遍历把整个tree都过一遍,对于每一个node,交换他的左右node就行了,唯一需要注意的就是不能用中序遍历罢了,因为中序遍历我们交换node实在中间,这样会导致接下来对root.right进行recursive call的时候相当于又对left做了一遍翻转

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
            
        def dfs(root):
            if not root:
                return
            temp = root.left
            root.left = root.right
            root.right = temp
            
            dfs(root.left)
            dfs(root.right)
        
        dfs(root)
        return root
讲解后:

这里看了卡哥讲解之后突然意识到自己之前为什么没想过如果解决中序遍历遇到的问题呢,卡哥的思路和对中序遍历会造成的问题我都理解和知道,但是卡哥提出的方案我直接傻掉了,对呀,既然中序遍历代表着我们处理完左子树之后右子树变到了左边,那么如果我们想要处理右子树,就应该在recursive call中再一次对root.left 进行递归就可以了呀


Leetcode 101. 对称二叉树

讲解前:

这道题因为之前做过所以还能写出来,但是其实每一次写的时候都还会有一点点懵,这次也一样,对于递归慢慢展开,没办法非常流畅的说清楚

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        
        def dfs(left, right):
            if not left and not right:
                return True
            elif not left or not right:
                return False
            
            val = left.val == right.val
            out = dfs(left.left, right.right)
            in_ = dfs(left.right, right.left)

            return val and out and in_ 
    
        return dfs(root.left, root.right)
讲解后:

听完卡哥的讲解醍醐灌顶,其实我在写法上有很大的问题才导致我知道题一直不太清晰,我没用搞懂对于节点的处理也就是前中后序的遍历在这道题中处在哪里,以及我们递归的部分,base case在哪里,recursive case在哪里

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        
        def dfs(left, right):
            if not left and not right:
                return True
            elif not left or not right:
                return False
            elif left.val != right.val:
                return False

            # post order traversal
            out = dfs(left.left, right.right)
            in_ = dfs(left.right, right.left)

            # do the compare
            return out and in_ 
    
        return dfs(root.left, root.right)

更加合理的写法应该是这样,我之前老是混淆对于遍历到的节点的处理和base case的问题,我们需要明白代码一开始的三个条件语句都是我们递归的base case,我们dfs函数的理念是判断当前的两个子树是不是可以对称的,也就是说但凡出现上面三种base case的任意一种,我们就已经reach到base然后可以返回了,明显就是不对称的,如果当前的左右节点没问题的话,我们就应该进入递归环节,也就是继续向下一层判断去看左右节点的in 和out是不是也是对称的,最后return 的阶段才是我们在对结果进行处理所以这里可以被称为后序遍历

导致我很困扰的还有一个问题就是这道题并不是传统意义上的递归遍历整个树,你会发现我们并不是一直先一直递归的树的低端然后慢慢通过返回一个一个递归函数然后得到答案的,这点其实源于我们的base case可以一直检查当前的两个左右节点param是不是已经满足了我们返回的条件,所以这个函数如果提前发现了左右子树没不可以对称了,那么是不会遍历剩下的节点的,同样,他是深度遍历的原因就是如果这个树的确是一个完全对称的树,我们就是会一直recursive到leaf node然后开始一直return true回去,最终得到一个true的结果
 

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值