代码随想录算法训练营第十二天| 226.翻转二叉树、 101. 对称二叉树、104.二叉树的最大深度、 111.二叉树的最小深度

写代码的第十二天

226.翻转二叉树

思路

看这个题的输出结果,第一时间想到的是层序遍历,将每层的数值翻转即可,但是这个是翻转二叉树啊!!!他不是翻转二叉树的值啊啊啊!!!所以要整体结点翻转!!!!!
一定要注意!!!!交换的是指针不是数值!!!!
错误第一版:只注意了数值的翻转,而不是结点的翻转,所以错了。

import collections
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        result = []
        queue = collections.deque()
        if root == None:
            return result
        queue.append(root)
        while len(queue) != 0:
            res = []
            for _ in range(len(queue)):
                node = queue.popleft()
                res.append(node.val)
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
            result.append(res[::-1])
        return result

正确代码

import collections
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        queue = collections.deque()
        if root == None:
            return root
        queue.append(root)
        while len(queue) != 0:
            for _ in range(len(queue)):
                node = queue.popleft()
                node.left,node.right = node.right,node.left
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
        return root

思路(递归)

递归的三个条件
1、参数和返回值是什么
2、终止条件是什么
3、单层逻辑是什么
解决问题1:参数和返回值是什么?在这里参数就是原始给的root,这个root不仅仅代表根结点,他是代表整个树中的所有结点,返回值也是root,因为我们没有重新设置一个空树然后插入结点,所有的操作都是在root上进行的,所以最后的返回值还是root。
解决问题2:终止条件是什么?其实就是当遇到空结点了,就终止递归。其实就是当遇到叶子结点了,这个时候没有什么可以交换的了,所以停止递归。
解决问题3:单层逻辑是什么?整体的思路是遍历到每个结点,然后对其左右孩子结点进行交换,也就是说要遍历二叉树的全部结点,这个时候我们会想到之前写过的前中后序遍历操作,所以在这里也是使用前中后序遍历都可以实现翻转二叉树。
前序遍历:中左右的顺序,也就是每次先遍历到的都是中间结点,然后针对这个结点对左右孩子进行交换翻转,然后对这个结点的左右孩子进行递归操作,所以递归的逻辑就是首先遍历中结点,然后递归操作翻转她的左右孩子结点。

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root == None:
            return root
        root.left,root.right = root.right,root.left
        self.invertTree(root.left)
        self.invertTree(root.right)
        return root

后序遍历:和前序遍历是一致的,只不过这次是左右中,也就是先递归左孩子右孩子,然后在进行中间结点的交换左右孩子。

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root == None:
            return root
        self.invertTree(root.left)
        self.invertTree(root.right)
        root.left,root.right = root.right,root.left
        return root

中序遍历:中序遍历大家很容易按照上面的前序和后序然后把交换那句放到递归中间,但是这是不对的!大家可以考虑一下如果先翻转了左孩子,然后遍历到中间结点,进行左右孩子的交换了,此时原本其左孩子就变成了右孩子,然后在进行左中右最后的右遍历,此时你遍历的还是左孩子!!!所以可以改成左中左或这右中右遍历。
左中左

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root == None:
            return root
        self.invertTree(root.left)
        root.left,root.right = root.right,root.left
        self.invertTree(root.left)
        return root

右中右

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root == None:
            return root
        self.invertTree(root.right)
        root.left,root.right = root.right,root.left
        self.invertTree(root.right)
        return root

101. 对称二叉树

思路(递归)

整体思路就是对比左子树的左孩子和右子树的右孩子是否相等,以及左子树的右孩子和右子树的左孩子是否相等,如果整颗树这两个条件都满足的话,就可以返回True了。
递归的三个条件
1、参数和返回值是什么
2、终止条件是什么
3、单层逻辑是什么
解决问题1:参数和返回值是什么?在这里参数就是原始给的root,这个root不仅仅代表根结点,他是代表整个树中的所有结点,返回值也是root,因为我们没有重新设置一个空树然后插入结点,所有的操作都是在root上进行的,所以最后的返回值还是root。
解决问题2:终止条件是什么?当左子树的左孩子==右子树的右孩子时才继续判断,除了这种情况的其他情况都立即终止。(左子树为空右子树不为空,右子树为空左子树不为空,左右子树都不为空但左子树的左孩子和右子树的右孩子值不相等,)
解决问题3:单层逻辑是什么?其实这里面不好理解的就是为什么这个是后续遍历,其实我们从根结点看就很容易理解了,如果要判断是否为对称二叉树,那么在跟结点中就要知道其左子树和右子树是不是对称的,那就要先判断左右子树是否是对称的,如果他们是对称的,反馈给跟结点,这时候才能说整个二叉树是对称二叉树。
解决问题4:为什么要单独写一个函数进行左右子树的比较?因为本题要判断的是整颗树是不是对称的,不是判断里面的子树是不是对称的,所以涉及到的问题是跟结点的左右子树是否对称,也就是这是两颗子树的比较,所以在isSymmetric函数中写是没有办法用递归的。
正确代码

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

思路(队列+迭代)

首先将跟结点的左右孩子结点入队,再将这两个结点出队,记录出队的结点,然后进行比较和判断,如果左孩子为空或者 右孩子为空那么返回false,如果左孩子和右孩子为空那么返回true,如果左孩子右孩子不为空但是数值不同,那么返回false,如果上述情况都不满足,将左子树的左孩子和右子树的右孩子,以及左子树的右孩子和右子树的左孩子入队,再次进行上述比较,一旦出现上述的情况就返回对应的true和false,但是当所有结点遍历完毕,也就是queue为空的时候了,也没有出现上述几种情况,那么返回true。
错误第一版:在判断这句话的时候,if left = = None and right == None: return True,会产生一个现象就是当只有一个结点满足这种情况的时候他也返回true,我们要做的是遍历完整个二叉树在做决定,他究竟是不是true。

import collections
class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        queue = collections.deque()
        if root == None:
            return True
        queue.append(root.left)
        queue.append(root.right)
        while len(queue) != 0:
            left = queue.popleft()
            right = queue.popleft()
            if left == None and right == None:
                return True
            elif left == None and right != None:
                return False
            elif  left != None and right == None:
                return False
            elif left.val != right.val:
                return False
            queue.append(left.left)
            queue.append(right.right)
            queue.append(left.right)
            queue.append(right.left)
        return True

正确代码

import collections
class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        queue = collections.deque()
        if root == None:
            return True
        queue.append(root.left)
        queue.append(root.right)
        while len(queue) != 0:
            left = queue.popleft()
            right = queue.popleft()
            if left == None and right == None:
                continue
            elif left == None and right != None:
                return False
            elif  left != None and right == None:
                return False
            elif left.val != right.val:
                return False
            queue.append(left.left)
            queue.append(right.right)
            queue.append(left.right)
            queue.append(right.left)
        return True

思路(栈+迭代)

只是换了一种数据结构,思路不变,代码几乎一致。

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        stack = []
        if root == None:
            return True
        stack.append(root.left)
        stack.append(root.right)
        while len(stack) != 0:
            left = stack.pop()
            right = stack.pop()
            if left == None and right == None:
                continue
            elif left == None and right != None:
                return False
            elif  left != None and right == None:
                return False
            elif left.val != right.val:
                return False
            stack.append(left.left)
            stack.append(right.right)
            stack.append(left.right)
            stack.append(right.left)
        return True

思路(层序)

和之前的层序遍历一样,只不过之前的是遇到某一孩子结点为空就直接过,这个里面要将这个空的赋值,然后最后输出的结果,如果每层的res[] == res[::-1]都满足这个条件,那么返回true,否则就是false。
错误第一版:其实不需要判断node.left != None和node.right != None,因为无论左右孩子是不是空都要放到队列里面,所以无论是什么结点直接就一致appendleft和right就好了。

import collections
class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        queue = collections.deque()
        if root == None:
            return True
        if root.left == None and root.right == None:
            return True
        queue.append(root)
        while len(queue) != 0:
            res = []
            for _ in range(len(queue)):
                node = queue.popleft()
                res.append(node.val)
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
                if node.left == None:
                    res.append(None)
                if node.right == None:
                    res.append(None)
            if res != res[::-1]:
                return False
        return True

错误第二版:node为空的情况没有排除啊啊啊啊啊啊!!!

import collections
class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        queue = collections.deque()
        if root == None:
            return True
        if root.left == None and root.right == None:
            return True
        queue.append(root)
        while len(queue) != 0:
            res = []
            for _ in range(len(queue)):
                node = queue.popleft()
                res.append(node.val)
                queue.append(node.left)
                queue.append(node.right)
            if res != res[::-1]:
                return False
        return True

代码随想录算法训练营第十二天| 226.翻转二叉树、 101. 对称二叉树、104.二叉树的最大深度、 111.二叉树的最小深度_第1张图片
正确代码

import collections
class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        queue = collections.deque()
        if root == None:
            return True
        if root.left == None and root.right == None:
            return True
        queue.append(root)
        while len(queue) != 0:
            res = []
            for _ in range(len(queue)):
                node = queue.popleft()
                if node != None:
                    res.append(node.val)
                    queue.append(node.left)
                    queue.append(node.right)
                else:
                    res.append(None)
            if res != res[::-1]:
                return False
        return True

104.二叉树的最大深度

区分一下深度和高度:从地面向上是高度,丛地面向下是深度,地面是1.

思路(递归)

昨天做的用的是层序遍历,今天用递归来一波。
总是有点不理解递归,那就写一下。我们想要做的事求二叉树的最大深度,那么就要知道其子树的最大深度在加一就行,如果要知道他子树的最大深度就要知道子树的子树的最大深度,这种情况就属于重复进行一个操作,也就是重复的计算某个子树的最大深度,然后加一,所以可以用递归。通过上面的流程可以看出,我们是最后才到跟结点的,之前一直找的都是他左子树和右子树的最大深度,所以可以看出应该用后序遍历,左右中。
正确代码

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        left = self.maxDepth(root.left)
        right = self.maxDepth(root.right)
        maxdepth = 1+max(left,right)
        return maxdepth

559. N 叉树的最大深度

思路

和上面的二叉树最大深度几乎一样。
错误第一版:root.children 的遍历:在 while 循环中,使用 root.children 进行遍历,但是 root.children 是一个列表,而不是单个节点。

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        left = self.maxDepth(root.left)
        right = self.maxDepth(root.right)
        maxdepth = 1+max(left,right)
        return maxdepth

正确代码

class Solution:
    def maxDepth(self, root: 'Node') -> int:
        depth = 0
        if root == None:
            return 0
        for child in root.children:
            dep = self.maxDepth(child)
            if dep > depth:
                depth = dep
        depth = 1 + depth
        return depth

111. 二叉树的最小深度

思路

找每个子树的最小深度,然后反馈给上一层,加一。
错误第一版:注意题目给的什么叫最小深度!!!跟结点到叶子结点的最小深度,也就是说如果跟结点的左子树或者右子树为空,最小深度不是1!!!!!

class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        left = self.minDepth(root.left)
        right = self.minDepth(root.right)
        mindep = min(left,right) + 1
        return mindep

正确代码

class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if root == None:
            return 0
        left = self.minDepth(root.left)
        right = self.minDepth(root.right)
        if root.left == None and root.right != None:
            return 1 + right
        if root.right == None and root.left != None:
            return 1 + left
        mindep = min(left,right) + 1
        return mindep

你可能感兴趣的:(代码随想录,算法,数据结构)