算法打卡day13

文章讲述了如何使用递归和迭代方法解决二叉树的层序遍历、翻转二叉树以及判断对称二叉树的问题,涉及LeetCode中的多个相关题目,强调了不同算法技巧的应用和层序遍历在这些问题中的关键作用。
摘要由CSDN通过智能技术生成

今日任务:

1)层序遍历10题

2)226翻转二叉树

3)对称二叉树

层序遍历

题目链接:

102. 二叉树的层序遍历 - 力扣(LeetCode)

107. 二叉树的层序遍历 II - 力扣(LeetCode)

199.二叉树的右视图(opens new window)

637.二叉树的层平均值(opens new window)

429.N叉树的层序遍历(opens new window)

515.在每个树行中找最大值(opens new window)

116.填充每个节点的下一个右侧节点指针(opens new window)

117.填充每个节点的下一个右侧节点指针II(opens new window)

104.二叉树的最大深度(opens new window)

111.二叉树的最小深度

文章讲解:代码随想录 (programmercarl.com)

102. 二叉树的层序遍历

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)

示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

class Solution:
    def levelOrder(self, root: [TreeNode]) -> list[list[int]]:
        if not root:
            return []

        queue = collections.deque([root])
        result = []

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

            result.append(level)

        return result

107. 二叉树的层序遍历 II

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]

把遍历Ⅰ得到的结果反转即可

class Solution:
    def levelOrderBottom(self, root: [TreeNode]) -> list[list[int]]:
        if not root:
            return []

        queue = collections.deque([root])
        result = []

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

            result.append(level)

        return result[::-1]

637.二叉树的层平均值

题目:给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。

示例 1:
输入:
root = [3,9,20,null,null,15,7]
输出:[3.00000,14.50000,11.00000]

只需要在遍历完一层后去平均即可

class Solution:
    def averageOfLevels(self, root: TreeNode) -> list[float]:
        if not root:
            return []

        queue = collections.deque([root])
        res =  []
        while queue:
            level_num = 0
            size = len(queue)
            for _ in range(size):
                cur = queue.popleft()

                level_num += cur.val

                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)

            res.append(level_num/size)

        return res

429.N叉树的层序遍历

​给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。

 与二叉树代码区别只是不再是左右节点,而变成孩子节点,其余不变

class Node:
    def __init__(self, val=None, children=None):
        self.val = val
        self.children = children
        
class Solution:
    def levelorder(self, root: Node) -> list[list[int]]:
        if not root:
            return []

        queue = collections.deque([root])
        result = []

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

                for child in cur.children:
                    queue.append(child)

            result.append(level)

        return result

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

这题只需要在遍历完每层节点后,取max

class Solution:
    def largestValues(self, root: [TreeNode]) -> list[int]:
        if not root:
            return []

        queue = collections.deque([root])
        result = []

        while queue:
            max_ = float('-inf')

            for _ in range(len(queue)):
                cur = queue.popleft()
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
                max_=max(max_,cur.val)

            result.append(max_)

        return result

116.填充每个节点的下一个右侧节点指针

给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next

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

        queue = collections.deque([root])
        while queue:

            pref = None
            for i in range(len(queue)):
                cur = queue.popleft()

                if pref:
                    pref.next = cur

                pref = cur

                if cur.left:
                    queue.append(cur.left)

                if cur.right:
                    queue.append(cur.right)

        return root

117.填充每个节点的下一个右侧节点指针II

与上一题完美二叉树一样的代码

class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next


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

        queue = collections.deque([root])
        while queue:

            pref = None
            for i in range(len(queue)):
                cur = queue.popleft()

                if pref:
                    pref.next = cur

                pref = cur

                if cur.left:
                    queue.append(cur.left)

                if cur.right:
                    queue.append(cur.right)

        return root

104.二叉树的最大深度

给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

用层序遍历的话,每遍历一层深度加1,直到结束即可

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
class Solution:
    def maxDepth(self, root: [TreeNode]) -> int:
        if not root:
            return 0

        queue = collections.deque([root])
        deepmax = 0

        while queue:
            deepmax += 1
            for _ in range(len(queue)):
                cur = queue.popleft()

                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)

        return deepmax


111.二叉树的最小深度

给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

遇上一题相似,还是采用层序遍历,每遍历一层深度加1,当遇见某一个节点左孩子与右孩子均为空则跳出

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    def minDepth(self, root: [TreeNode]) -> int:
        if not root:
            return 0

        queue = collections.deque([root])
        minmax = 0

        while queue:
            minmax += 1
            for _ in range(len(queue)):
                cur = queue.popleft()

                if not cur.left and not cur.right:
                    return minmax

                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)

        return minmax

感想:昨天已经详细讲了层序遍历,这几题基本是套模板,今天就不再重复了,每一题在构建层序遍历上稍有改动

226翻转二叉树 (优先掌握递归)

题目链接:226. 翻转二叉树 - 力扣(LeetCode)

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

文章讲解:代码随想录 (programmercarl.com)

视频讲解:听说一位巨佬面Google被拒了,因为没写出翻转二叉树 | LeetCode:226.翻转二叉树哔哩哔哩bilibili

递归法(前序)思路:

这个思路比较好想,采用递归要明确递归三部曲

1.确定递归函数的参数和返回值

参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。

返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode。

2.确定终止条件

当前节点为空的时候,就返回

if not root : return root

3.确定单层递归的逻辑

因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。

root.left, root.right = root.right, root.left

invertTree(root.left);
invertTree(root.right);

基于这递归三步法,代码基本写完

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    # 递归法,前序
    def invertTree(self, root: [TreeNode]) -> [TreeNode]:
        if not root:
            return root

        root.left, root.right = root.right, root.left  # 中
        self.invertTree(root.left)  # 左
        self.invertTree(root.right)  # 右

        return root

迭代法,用栈实现

采用前序,中左右

由于栈结构特性,先进后出,所有我们想要左节点先出来,再加入栈中的时候就先加加入右节点,在用栈实现二叉树过程已经记录过了

这题主要就是在于将栈中弹出的这个节点反转

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    # 迭代法,用栈实现
    def invertTree2(self, root: [TreeNode]) -> [TreeNode]:
        if not root:
            return root

        stack = [root]
        while stack:
            node = stack.pop()

            node.left, node.right = node.right, node.left  # 中

            if node.right:
                stack.append(node.right)  # 右

            if node.left:
                stack.append(node.left)  # 左

        return root

感想:

法一中,只要找准递归三要素,法二中只要掌握用栈实现二叉树过程,做好这两点这题其实就没什么难度了 ,主要前面也练习了不少题,慢慢的二叉树实现过程就越来越熟练了

101对称二叉树 (优先掌握递归)

题目链接:101. 对称二叉树 - 力扣(LeetCode)

题目:给定一个二叉树,检查它是否是镜像对称的

示例 1
输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2
输入:root = [1,2,2,null,3,null,3]
输出:false

文章讲解:代码随想录 (programmercarl.com)

视频讲解:新学期要从学习二叉树开始! | LeetCode:101. 对称二叉树哔哩哔哩bilibili

思路:

这题要明白:不是某一个节点的左节点与右节点比较,而是左子树与右子树对称

相对于外侧来说,也就是左子树的左节点与右子树的右节点一样

相对于内测来说,就是左子树的右节点与右子树的左节点一样

比较的两个节点,其中有任意一个节点为空,则不对称。或者两个节点均为空,则对称

法一:递归

1.确定递归函数的参数和返回值

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

返回值自然是bool类型。

compare(left,right)

2.确定终止条件

要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。

节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左边节点右边节点

  • 左边节点为空,右边节点不为空,不对称,return false
  • 左不为空,右为空,不对称 return false
  • 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

  • 左右都不为空,比较节点数值,不相同就return false

此时左右节点不为空,且数值也不相同的情况我们也处理了。

3.确定单层递归的逻辑

此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

  • 比较二叉树外侧是否对称:传入的是左边节点的左孩子,右边节点的右孩子。
  • 比较内侧是否对称,传入左边的右孩子,右边节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    # 递归
    def isSymmetric(self, root: [TreeNode]) -> bool:
        if not root:
            return True

        return self.compare(root.left, root.right)

    def compare(self, left: [TreeNode], right: [TreeNode]):
        if not left and not right:
            return True
        elif not left and right:
            return False
        elif left and not right:
            return False
        elif left.val != right.val:
            return False

        outside = self.compare(left.left, right.right)  # 左子树左节点,右指数右节点
        inside = self.compare(left.right, right.left)  # 左字数右节点,右指数左节点

        return outside and inside

法二:迭代,用队列实现

遍历每层,将其子节点加入队列时,注意是将需要比较的两个节点,放在一起

比如:先加外侧节点,再加内测节点。或者反过来,先加内侧节点,在家外侧节点

核心就是要将比较的两个节点放一起,连续弹出两个数,比较其是否相等即可

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    # 迭代:使用队列
    def isSymmetric2(self, root: [TreeNode]) -> bool:
        if not root:
            return True

        queue = collections.deque()

        queue.append(root.left)  # 将左子树头结点加入队列
        queue.append(root.right)  # 将右子树头结点加入队列

        while queue:
            leftNode = queue.popleft()
            rightNode = queue.popleft()

            # 左节点为空、右节点为空,此时说明是对称的
            if not leftNode and not rightNode:
                continue

            # 左右一个节点不为空,或者都不为空但数值不相同,返回false
            if not leftNode or not rightNode or leftNode.val != rightNode.val:
                return False

            queue.append(leftNode.left)  # 加入左节点左孩子
            queue.append(rightNode.right)  # 加入右节点右孩子
            queue.append(leftNode.right)  # 加入左节点右孩子
            queue.append(rightNode.left)  # 加入右节点左孩子

        return True

法三迭代:用栈实现

思路与法二一样,只是换了一种数据结构,但是只要保证比较的两个节点挨在一起就行

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    # 迭代:使用栈
    def isSymmetric3(self, root: [TreeNode]) -> bool:
        if not root:
            return True

        stack = []

        stack.append(root.left)
        stack.append(root.right)

        while stack:
            leftNode = stack.pop()
            rightNode = stack.pop()

            # 左节点为空、右节点为空,此时说明是对称的
            if not leftNode and not rightNode:
                continue

            # 左右一个节点不为空,或者都不为空但数值不相同,返回false
            if not leftNode or not rightNode or leftNode.val != rightNode.val:
                return False

            stack.append(leftNode.left)  # 加入左节点左孩子
            stack.append(rightNode.right)  # 加入右节点右孩子
            stack.append(leftNode.right)  # 加入左节点右孩子
            stack.append(rightNode.left)  # 加入右节点左孩子

        return True

法四:迭代,用层序遍历实现

跟前面几种有一点不一样,层次遍历是按层遍历,我们可以将一层的结果放入列表中,比较每一层列表反转后是否一样即可,这里需要注意的是,存入列表时,不存在的要用None占位

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    # 层次遍历
    def isSymmetric4(self, root: [TreeNode]) -> bool:
        if not root:
            return True

        queue = collections.deque([root.left,root.right])

        while queue:
            queue_size = len(queue)
            # 直接判断 queue 的长度是不是偶数,不是则False
            if queue_size % 2 != 0:
                return False

            level = []
            for i in range(queue_size):
                node = queue.popleft()

                # 添加元素到队列,没有要添加空值占位
                if node:
                    level.append(node.val)
                    queue.append(node.left)
                    queue.append(node.right)
                else:
                    level.append(None)

            if level != level[::-1]:
                return False

        return True

感想:这几题都很好的展示了几种不同数据结构实现二叉树的特点,如果可以用层序遍历,那基本就是套模板。采用递归要注意递归三要素,采用迭代要注意前中后序遍历代码不一样类似,需要自己理清楚

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值