代码随想录算法训练营第十一天| 二叉树理论基础、 递归遍历、迭代遍历、 统一迭代、层序遍历

写代码的第十一天,进入二叉树了

二叉树的定义

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

递归遍历

思路

1、确定递归函数的参数和返回值;
2、确定终止条件
3、确定单层递归的逻辑
解决问题1:参数和返回值?我们将root根结点作为参数,进行遍历,设置一个空列表,用来存储最后的输出结果返回值。
解决问题2:终止条件是什么?从跟结点开始遍历,那么当遍历到空的时候停止遍历。
解决问题3:单层递归逻辑?前序遍历:中左右;中序遍历:左中右;后续遍历:左右中。
前序遍历:
错误第一版:res没跟新,每次遍历完没有加入到res中。

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        if root == None:
            return res
        res.append(root.val)
        self.preorderTraversal(root.left)
        self.preorderTraversal(root.right)
        return res

正确版

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        if root == None:
            return res
        res.append(root.val)
        res += self.preorderTraversal(root.left)
        res += self.preorderTraversal(root.right)
        return res

后序遍历:
正确代码

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        if root == None:
            return res
        res += self.postorderTraversal(root.left)
        res += self.postorderTraversal(root.right)
        res.append(root.val)
        return res

中序遍历

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        if root == None:
            return res
        res += self.inorderTraversal(root.left)
        res.append(root.val)
        res += self.inorderTraversal(root.right)
        return res

迭代遍历(非递归)

一般来说都可以用栈实现递归的效果。

前序遍历(栈)

思路

首先要有一个栈,用栈来存储树中的结点,还需要一个数组作为最后的返回值输出。那么栈的作用其实就是一个存储中间转换值的作用,对于前序遍历来说是中左右的顺序,那么先入栈的是中,按理来说接下来入栈的应该是中的左孩子,然后才是有孩子,但是需要考虑一个问题栈的特性是后进先出,所以为了让最后的输出先是左孩子,再是右孩子,那么就需要在栈中先入栈右孩子,再入栈左孩子,这样从栈中输出的就先是左孩子,然后是右孩子。
错误第一版:错误原因,没有判断如果结点为空该怎么办。同理,下面的左右孩子结点也要判断是否为空。

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        stack.append(root.val)
        num = stack.pop()
        res.append(num)
        stack.append(root.right.val)
        stack.append(root.left.val)
        numleft = stack.pop()
        res.append(numleft)
        numright = stack.pop()
        res.append(numright)
        return res

t
错误第二版:错误原因是当前的代码判断的都是root根结点的,如果根结点只有左孩子呢,或者只有右孩子呢,那么这个时候pop他无结点的那一部分就会报错,所以要用循环,要循环的是全部的结点,而不是root结点自己!!!!
但是循环的终止条件是什么?也就是所有结点全部使用完毕,可能第一想法是node.left或node.right为空那么停止循环,但是这个问题和前面一样,谁也不能保证这个结点有没有左右孩子结点,如果用这个作为循环条件会导致最后丢结点的。所以现在就只剩一个条件可以用了,也就是栈是否为空,如果栈已经为空了,那一定是所有元素都pop出来了。

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root == None:
            return res
        stack.append(root.val)
        num = stack.pop()
        res.append(num)
        if root.right != None:
            stack.append(root.right.val)
        if root.left != None:
            stack.append(root.left.val)
        numleft = stack.pop()
        res.append(numleft)
        numright = stack.pop()
        res.append(numright)
        return res

在这里插入图片描述
错误第三版:这个测试用例无法通过,可以发现结点的数字不进循环呀,代码中虽然加入了循环,但是还是用的root结点这不对,我们使用的是当栈不为空的时候进入循环,在这段代码中我们将root加入栈但是随后我们就pop出来了,这时候栈一定是空的了!!!!!那么是根本不进入while循环的!!!!!!!所以要进入循环之后再存储和pop。而且还有一个问题,我们一直append进入栈的都是val,那么就无法用栈中的结点进行左右孩子的判断,所以append进入栈的应该是完整的结点,而不是单独的val。

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root == None:
            return res
        stack.append(root.val)
        num = stack.pop()
        res.append(num)
        while len(stack) != 0:
            if root.right != None:
                stack.append(root.right.val)
            if root.left != None:
                stack.append(root.left.val)
            numleft = stack.pop()
            res.append(numleft)
            numright = stack.pop()
            res.append(numright)
        return res

在这里插入图片描述
错误第四版:while循环中的if判断可以判断是否num的左右孩子为空在进行入栈操作,但是在这个if之后我们要进行结点的pop以及val值的append,那么也是需要判断pop时候stack是否为空,但是我们会发现,下面的四行pop结点append进res和上面while循环开始时要做的操作是一致的,我们不需要重新对左右孩子结点进行操作,所有的操作都是对结点的饿操作,只需要判断左右孩子结点是否存在即可,若存在按顺序输出。

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root == None:
            return res
        stack.append(root)
        while len(stack) != 0:
            num = stack.pop()
            res.append(num.val)
            if num.right != None:
                stack.append(num.right)
            if num.left != None:
                stack.append(num.left)
            numleft = stack.pop()
            res.append(numleft)
            numright = stack.pop()
            res.append(numright)
        return res

在这里插入图片描述
正确版本

class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root == None:
            return res 
        stack.append(root)
        while len(stack) != 0:
            num = stack.pop()
            res.append(num.val)
            if num.right != None:
                stack.append(num.right)
            if num.left != None:
                stack.append(num.left)
        return res

后序遍历(栈)

思路

在前序遍历的思路上,在栈这种数据结构中,我们的输入顺序是中,弹出中,然后输入右左,弹出左右,得到的顺序是中左右。对于后序遍历来说是左右中,那是不是将前序遍历反转一下就可以呢?我们将前序遍历简单修改一下,首先在栈中输入中,弹出中,然后输入左右,这样弹出的是右左,最后的结果就是中右左,然后反转一下也就变成了左右中,正好是后续遍历。

class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        if root == None:
            return res
        stack.append(root)
        while len(stack) != 0:
            node = stack.pop()
            res.append(node.val)
            if node.left != None:
                stack.append(node.left)
            if node.right != None:
                stack.append(node.right)
        return res[::-1]

中序遍历(栈+指针)

前序和后序遍历都是首先访问的是哪个结点,那么首先处理的就是哪个结点,但是中序遍历不是,对于二叉树遍历来说,首先遍历的肯定是二叉树的根结点,但是在中序遍历中最先处理的却不是跟结点,也就是遍历结点和处理结点不一致,所以和上面两个遍历操作是不一样的。

思路

还是用栈实现,这里的栈是存储遍历过但还没用上的结点。首先设置一个指针,这个指针的作用是一直向下遍历,因为中序遍历就是左中右,所以要先找到最下面的左孩子,所以这个指针的停止条件就是,当他为空的时候就证明已经到了最下面的左孩子了,这个时候栈里面的元素就是指针遍历经过的所有元素了,所以此时左孩子为空的结点已经找到了,那么要遍历中结点了,那中结点其实就是这个指针的上一位结点,也就是在栈中存储的栈顶结点,所以pop这个结点出来就是中结点,然后指针继续遍历这个中结点的右孩子,如果存在右孩子,那么入栈,然后pop出来,如果不存在右孩子,那直接将栈中的栈顶元素再pop出来(因为现在没有右孩子了,就属于左中右已经遍历结束了,需要向父结点继续遍历了,此时的父结点就是栈顶元素,画个图就明白了)。
需要注意一个问题:在这里设置的指针是遍历结点的,并不是只遍历左孩子结点!!!!右孩子也管!!!!。
错误第一版:最后的输出结果都是[],也就是说res.append完全没用!推断错误原因在判断右孩子那里,在else判断中我设置了node来存储stack的栈顶元素然后将其加入到res中,接下来使用的却是cur的right,但是此时cur是什么???在本题中我们一直使用的是cur来判断当前遍历的结点,所以在else中也应该是用cur来存储现在要弹出的是哪个结点,然后判断的是这个结点之后的right。

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        cur = root
        if root == None:
            return res
        while cur !=  None and len(stack) != 0:
            if cur != None:
                stack.append(cur)
                cur = cur.left
            else:
                node = stack.pop()
                res.append(node.val)
                cur = cur.right
        return res

错误第二版:输出结果和上面一样。发现问题在whilt循环中!!!while cur != None and len(stack) != 0:在这里我们用的是and len(stack) != 0但是在这句话之前并没有任何元素入栈,栈一直是空的,所以while循环根本没用上。

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        cur = root
        if root == None:
            return res
        while cur !=  None and len(stack) != 0:
            if cur != None:
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()
                res.append(cur.val)
                cur = cur.right
        return res

正确代码

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        res = []
        stack = []
        cur = root
        if root == None:
            return res
        while cur !=  None or len(stack) != 0:
            if cur != None:
                stack.append(cur)
                cur = cur.left
            else:
                cur = stack.pop()
                res.append(cur.val)
                cur = cur.right
        return res

层序遍历

思路

之前都是栈来实现,这个层序是用队列来实现的,就是先将第一层的结点加入到队列中,然后弹出,弹出结点的左右孩子按顺序加入到队列中,然后继续弹出结点在加入对应结点的左右孩子结点。但是我们要输出的是一个二位数据,也就是说每一层一个数组,所以需要判断哪些是一层呢?所以需要知道每层有多少个元素。
错误第一版:输出结果长这样。分析错误地方在于根本没区分出来每一层,虽然数值对了,但是都是在一层。所以发现问题在while len(queue) != 0:用这句话作为每一层的判断是不对的,因为queue只有当所有结点都输出结束才会变成空的,也就是说它里面一直是存在结点的,而且不一定是哪一层的。
注意:队列是先进先出的,也就是说是在队列的最左侧弹出元素,popleft,但是popleft只能在collections.deque()中才能用。所以要加import collections,并且queue的初始化应该是queue = collections.deque()。

import collections
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        result = []
        queue = collections.deque()
        if root == None:
            return result
        queue.append(root)
        while len(queue) != 0:
            res = []
            while 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)
        return result

在这里插入图片描述
正确代码

import collections
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        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)
        return result

开始1打10了奥

107. 二叉树的层序遍历 II

思路

和前面的一样一样的,最后的result反转一下就行。

import collections
class Solution:
    def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        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)
        return result[::-1]

199. 二叉树的右视图

import collections
class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        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

637. 二叉树的层平均值

import collections
class Solution:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        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)
            average = sum(res) / len(res)
            result.append(average)
        return result

429. N 叉树的层序遍历

错误第一版:这是一个N叉树,所以加入queue队列的就不是只有一个左孩子或者又孩子。所以在判断这里的时候if node.children != None:,应该是queue.extend(node.children)
注意:append和extend区别:queue.append(item): 这个方法用于将单个元素 item 添加到队列的右侧(末尾)。例如,queue.append(10) 将会将元素 10 添加到队列的末尾。
queue.extend(iterable): 这个方法用于将可迭代对象 iterable 中的元素逐个添加到队列的右侧(末尾)。例如,queue.extend([20, 30, 40]) 将会将列表 [20, 30, 40] 中的元素逐个添加到队列的末尾。

import collections
class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        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.children != None:
                    queue.append(node.children)
            result.append(res)
        return result

正确代码

import collections
class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        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.children != None:
                    queue.extend(node.children)
            result.append(res)
        return result

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

import collections
class Solution:
    def largestValues(self, root: Optional[TreeNode]) -> List[int]:
        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)
            maxnum = max(res)
            result.append(maxnum)
        return result

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

思路

乍一看我没明白这题让干啥,仔细一看其实就是给每个结点加一个self.next = next指向其右结点,所以其实也是层序遍历,只是在遍历到每个结点的时候对他的self.next进行操作,指向它的下一个结点。所以需要设置一个pre结点初始化为None,因为要初始化为每一层第一个结点的前面。
错误第一版:错误原因是没有判断pre是不是None,如果是空是没有next的。

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

正确代码

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

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

正确代码:(其实和上面一样)

import collections
class Solution:
    def connect(self, root: 'Node') -> 'Node':
        queue = collections.deque()
        if root == None:
            return root
        queue.append(root)
        while len(queue):
            pre = None
            for _ in range(len(queue)):
                node = queue.popleft()
                if pre:
                    pre.next = node
                pre = node
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
        return root

104. 二叉树的最大深度

import collections
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        queue = collections.deque()
        result = []
        if root == None:
            return 0
        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)
        return len(result)

111. 二叉树的最小深度

思路

其实最开始想的是深度遍历,但是既然这个题出现在层序遍历中,那么一定用层序能解决。
终止条件是当某一结点的左右孩子都为空的时候,就是最小深度了。

import collections
class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        depth = 0
        queue = collections.deque()
        if root == None:
            return 0
        queue.append(root)
        while len(queue) != 0:
            depth += 1
            for _ in range(len(queue)):
                node = queue.popleft()
                if node.left == None and node.right == None:
                    return depth
                if node.left != None:
                    queue.append(node.left)
                if node.right != None:
                    queue.append(node.right)
        return depth
  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值