首页 > 编程语言 >代码随想录算法训练营第十二天|二叉树理论基础|二叉树的递归遍历|二叉树的迭代遍历|二叉树的统一迭代法|二叉树的层序遍历

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

时间:2024-11-25 17:32:41浏览次数:7  
标签:node None 遍历 val 随想录 que 二叉树 root left

二叉树的理论基础

二叉树的主要形式:

        二叉树有两种主要的形式:满二叉树和完全二叉树;

        满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。可以说深度为k,有2^k-1个节点的二叉树。

        完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。(有左子树可以,没有左子树,光有右子树的不是完全二叉树)

        二叉搜索树:二叉搜索树是一个有序树;

                * 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
                * 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
                * 它的左、右子树也分别为二叉排序树;

        平衡二叉树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

二叉树的存储方式:

        二叉树可以链式存储,也可以顺序存储。(链式存储方式就用指针, 顺序存储的方式就是用数组)

        链式存储:通过节点元素的左指针和右指针分别对应左子树和右子树)

        顺序存储:如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

二叉树的遍历方式

        二叉树主要有2种遍历方式:深度优先遍历和广度优先遍历;

        1、深度优先遍历

  •               前序遍历(递归法,迭代法)
    •        中序遍历(递归法,迭代法)
      • 后序遍历(递归法,迭代法)

 这里前中后,其实指的就是中间节点的遍历顺序,只要大家记住 前中后序指的就是中间节点的位置就可以了。

          2、广度优先遍历

                层次遍历(迭代法) 

二叉树的定义

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

二叉树的递归遍历

递归:每次写递归,都按照这三要素来写,可以保证大家写出正确的递归算法!

1、确定递归函数的参数和返回值:确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

2、确定终止条件: 写完了递归算法,运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

3、确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

144. 二叉树的前序遍历 - 力扣(LeetCode)

        前序:先遍历节点,在左子树,在右子树先遍历节点,在左子树,在右子树

from typing import List
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []

        def dfs(node):
            if node is None:
                return
            res.append(node.val)
            dfs(node.left)
            dfs(node.right)
        dfs(root)
        return res

if __name__ == '__main__':
    null = None
    root = root = [1, null, 2, 3]
    root = generate_tree(root)
    print('前序遍历:')
    res = Solution().preorderTraversal(root) # 9 3 15 20 7
    print(res)

145. 二叉树的后序遍历 - 力扣(LeetCode)

        后序:先左子树,在右子树,最后节点;

from typing import List
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []

        def dfs(node):
            if node is None:
                return
            dfs(node.left)
            dfs(node.right)
            res.append(node.val)

        dfs(root)
        return res

if __name__ == '__main__':
    null = None
    root = root = [1, null, 2, 3]
    root = generate_tree(root)
    print('后序遍历:')
    res = Solution().inorderTraversal(root)
    print(res)

94. 二叉树的中序遍历 - 力扣(LeetCode)

        中序:先左子树,在节点,最后在右子树;

from typing import List
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 postorderTraversal(self, root: TreeNode) -> List[int]:
        res = []

        def dfs(node):
            if node is None:
                return
            dfs(node.left)
            res.append(node.val)
            dfs(node.right)

        dfs(root)
        return res

if __name__ == '__main__':
    null = None
    root = root = [1, null, 2, 3]
    root = generate_tree(root)
    print('中序遍历:')
    res = Solution().postorderTraversal(root)
    print(res)

二叉树的迭代遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

        前序遍历(迭代法):前序遍历顺序是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈中(因为出栈的时候顺序才是中左右),再加入左孩子;

from typing import List
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 preorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        stack = [root]
        res = []
        while stack:
            # 先将根节点的进行弹出;后续弹出2层节点(子树)
            node = stack.pop()
            # 弹出的值进行保存;
            res.append(node.val)
            # 先保存右子树
            if node.right:
                stack.append(node.right)
            # 再保存左子树
            if node.left:
                stack.append(node.left)
        return res


if __name__ == '__main__':
    null = None
    root = root = [1, null, 2, 3]
    root = generate_tree(root)
    print('前序遍历:')
    res = Solution().preorderTraversal(root)
    print(res)

145. 二叉树的后序遍历 - 力扣(LeetCode)

        后序遍历可以从前序遍历中适当调整,将顺序改成(中右左)然后反向,就成了左右中的后序遍历,所以需要在前序中先处理根节点,在处理左孩子,最后右孩子;最后反向完成;

from typing import List
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 postorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        stack = [root]
        res = []
        while stack:
            # 先将根节点的进行弹出;后续弹出2层节点(子树)
            node = stack.pop()
            # 弹出的值进行保存;
            res.append(node.val)
            # 先保存左子树
            if node.left:
                stack.append(node.left)
            # 再保存右子树
            if node.right:
                stack.append(node.right)
        return res[::-1]

if __name__ == '__main__':
    null = None
    root = root = [1, null, 2, 3]
    root = generate_tree(root)
    print('后序遍历:')
    res = Solution().postorderTraversal(root)
    print(res)

94. 二叉树的中序遍历 - 力扣(LeetCode)

中序遍历与前序和后序遍历不同,不能提前将根节点放入栈中,因为中序遍历顺序是左中右;

操作:

        1、处理:将元素放入result数组中;

        2、访问:遍历节点

在使用迭代法写中序遍历,就需要借助指针的遍历来帮忙访问节点,栈则用来处理节点上的元素;

from typing import List
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 inorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        # 不能提前将root节点加入stack中
        stack = []
        res = []
        cur = root
        while cur or stack:
            # 先访问左节点
            if cur:
                stack.append(cur)
                cur = cur.left
            # 再处理顶节点和右节点
            else:
                cur = stack.pop()
                res.append(cur.val)
                cur = cur.right
        return res


if __name__ == '__main__':
    null = None
    root = root = [1, null, 2, 3]
    root = generate_tree(root)
    print('中序遍历:')
    res = Solution().inorderTraversal(root)
    print(res)

二叉树的统一迭代法

前序遍历:

class Solution:
    # 前序遍历
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node != None:
                if node.right:
                    stack.append(node.right)
                if node.left:
                    stack.append(node.left)
                stack.append(node)
                stack.append(None)
            else:
                node = stack.pop()
                res.append(node.val)
        return res

中序遍历:

class Solution:
    # 中序遍历
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node != None:
                if node.right: # 右
                    stack.append(node.right)
                stack.append(node) # 中
                stack.append(None)
                if node.left: # 左
                    stack.append(node.left)
            else:
                node = stack.pop()
                res.append(node.val)
        return res

后序遍历:

class Solution:
    # 后序遍历
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        result = []
        stack = []
        if root:
            stack.append(root)
        while stack:
            node = stack.pop()
            if node != None:
                stack.append(node)  # 中
                stack.append(None)

                if node.right:  # 右
                    stack.append(node.right)
                if node.left:  # 左
                    stack.append(node.left)
            else:
                node = stack.pop()
                result.append(node.val)
        return result

二叉树的层序遍历

        层序遍历需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。(而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。)

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

该题可以作为层序遍历的模板

from typing import List, Optional
from collections import deque
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        queue = deque([root])
        res = []
        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)
            res.append(level)
        return res


if __name__ == '__main__':
    null = None
    root = [3,9,20,null,null,15,7]
    root = generate_tree(root)
    res = Solution().levelOrder(root)
    print(res)

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

        与LC102一样思路,就是把结果的数组进行反转就行

from typing import List, Optional
from collections import deque
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        queue = deque([root])
        res = []
        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)
            res.append(level)
        return res[::-1]


if __name__ == '__main__':
    null = None
    root = [3, 9, 20, null, null, 15, 7]
    root = generate_tree(root)
    res = Solution().levelOrderBottom(root)
    print(res)

199. 二叉树的右视图 - 力扣(LeetCode)

         思路:层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是,就放进result数组中,随后返回result就可以了。

from typing import List, Optional
from collections import deque
# 二叉树生成代码
def generate_tree(vals):
    if len(vals) == 0:
        return None
    que = [] # 定义队列
    fill_left = True # 由于无法通过是否为 None 来判断该节点的左子树是否可以填充,用一个记号判断是否需要填充左节点
    for val in vals:
        node = TreeNode(val) if val else None # 非空值返回节点类,否则返回 None
        if len(que)==0:
            root = node # 队列为空的话,用 root 记录根结点,用来返回
            que.append(node)
        elif fill_left:
            que[0].left = node
            fill_left = False # 填充过左子树后,改变记号状态
            if node: # 非 None 值才进入队列
                que.append(node)
        else:
            que[0].right = node
            if node:
                que.append(node)
            que.pop(0) # 填充完右子树,弹出节点
            fill_left = True #
    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 rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        queue = deque([root])
        # 右视图
        right_view = []
        while queue:
            l = len(queue)
            for i in range(l):
                node = queue.popleft()
                # 判断是否遍历到单层的最后面的元素
                if i == l - 1:
                    right_view.append(node.val)

                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        return right_view


if __name__ == '__main__':
    null = None
    root = [1,2,3,null,5,null,4]
    root = generate_tree(root)
    res = Solution().rightSideView(root)
    print(res)

        层序遍历先写3题,其他的后序补充

标签:node,None,遍历,val,随想录,que,二叉树,root,left
From: https://blog.csdn.net/weixin_49494409/article/details/144021096

相关文章

  • 617. 合并二叉树 Golang实现
    题目描述:给你两棵二叉树:root1和root2。想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为null的节点将直接作......
  • 代码随想录之滑动窗口、螺旋矩阵、区间和、开发商土地;Java之数据结构、集合源码、File
    代码随想录滑动窗口1、如果给两个字符串s和t,判断t是否为s的子串或是否s包含t的排列,用t的长度固定滑动窗口的大小,初始化将s的前t.length()个长度的字符情况存储在int数组中,int数组的大小由字符串中字符的类型决定,最大为ascii表的长度,为128。  每次循环滑动窗口向前移一位,即lef......
  • 代码随想录:快乐数
    代码随想录:快乐数这题主要是学习一下几种set怎么用。三种set,第一种第二种都是有序的,注意这个序列和插入序列无关,只和插入元素本身有关。第三种哈希表,无序,如果只需要找元素是否出现过,用第三种刚刚好。集合底层实现是否有序数值是否可以重复能否更改数值查询效率增删效......
  • 树的遍历顺序及其应用
    树的遍历顺序及其应用一、DFS序DFS序就是以DFS的方式,记录每一个节点第一次被访问的顺序,这种顺序形成一个形成一个长度为\(n\)的序列。主要被用来维护子树信息。有以下特点:对于任意一个点来说,其子树里所有点的DFS序是连续的,具体来讲,\(x\)的子树的所有结点的DFS序......
  • Python内置数据结构:列表篇:【】,list函数创建。列表创建常见遇到问题,索引,列表增删改查,常
    介绍:列表元组字典集合列表: 方括号【】和list函数是列表最常用的两种定义方式。我们暂且称列表内的东西为元素,列表内的元素以逗号分隔开。列表的初始化:空列表,有数值是列表的两种初始化情况。使用方括号创建列表:【】a=[]#创建了一个空列表并将其赋值给了a我们可以称a为一......
  • 代码随想录算法训练营第十一天|LC150.逆波兰表达式求值|LC239.滑动窗口最大值|LC347.
    150.逆波兰表达式求值-力扣(LeetCode)题目要求:    1、整数除法只保留整数部分;    2、该表达式总会得出有效数值且部存在除数为0的情况;    3、逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。fromtypingimportListfromoperato......
  • 遍历 hashmap 的三种方式
    1.使用entrySet()遍历HashMap1.1概述entrySet()方法返回HashMap中所有键值对的集合。每个键值对被封装成一个Map.Entry对象。使用entrySet()可以方便地同时获取键和值,是最常用的遍历HashMap的方式之一。1.2示例代码以下是使用entrySet()遍历HashMap......
  • 代码随想录算法训练营第二十五天|LeetCode491.递增子序列、46.全排列、47.全排列II、3
    前言打卡代码随想录算法训练营第49期第二十五天  ○(^皿^)っHiahiahia…首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的训练营大家庭一起进步。今......
  • 科普文:软件架构之Linux系列【linux内核数据结构:链表、队列、映射、二叉树】
    概叙科普文:软件架构之Linux系列【linux内核数据结构汇总】-CSDN博客Linux内核提供了许多复杂的数据结构,这些结构被广泛用于各种不同的目的,例如存储设备管理、内存管理、进程管理等。以下是一些常见的数据结构以及它们的简要描述:双向链表(list):实现链表的数据结构,每个节点都......
  • 代码随想录刷题学习日记
    仅为个人记录复盘学习历程,解题思路来自代码随想录代码随想录刷题笔记总结网址:代码随想录376.摆动序列如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。给定一个整数序列,......