算法训练第十四天 | 二叉树 理论基础 递归遍历 迭代遍历 统一迭代

一、二叉树理论基础

二叉树有哪些类型?

二叉树主要分为:
1、满二叉树:只有度为0和2的结点,且度为0的结点在同一层上(深度为k,节点个数为2^k-1)
2、完全二叉树:除了底层节点,每层节点都达到了最大值(保证父子节点的顺序关系)

两者都是无值的,谈不上是不是有序树,就是个形

· 二叉搜索树(/二叉排序树):有值的,有序树
① 左子树不为空,则左子树的值均小于根节点
② 右子树不为空,则右子树的值均大于根节点
③ 子树也是二叉搜索树

· 平衡二叉搜索树(/AVL树):有值的,有序树
① 左右子树高度差值绝对值<1
② 子树也是平衡二叉树
③ 可以是空树

怎么存储二叉树?

存储方式分为:
1、链式存储:即指针 (一般选这个)
2、顺序存储:即数组
(数组的父节点是i,左孩子就是2i+1,右孩子就是2i+2)

如何遍历二叉树?

二叉树遍历方式主要分为
1、深度优先遍历

  • 前序遍历(递归法、迭代法) 中左右
  • 中序遍历(递归法、迭代法) 左中右
  • 后序遍历(递归法、迭代法) 左右中

2、广度优先遍历

  • 层序遍历(迭代法)

迭代的实现使用栈,栈的结构先进后出符合迭代思想
广度遍历的实现使用队列,队列先进先出满足层级遍历思想

迭代和递归区别:
递归是重复调用函数自身
迭代是循环内保存的变量作为参数再次进入循环

如何定义一个二叉树?

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

二、二叉树的递归遍历和栗子

用栈表示递归,进行深度优先遍历

写递归前,先确定递归三要素:
1、我这个递归函数什么时候结束
2、我这个递归函数的流程是什么
3、我这个递归函数要传入什么参数,有返回的结果吗

例如前序遍历:
1、当遍历结束为空时,这个函数就结束了if (root == None): return
2、前序遍历思想中左右
result.append(root.val) traversal(root.left) traversal(root.right)
3、递归传入的参数就是某个节点,因为是做遍历所以没有返回值def traversal(root)

栗子有leetcode 145,完整前序遍历如下:

# Definition for a binary tree node.
# 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: Optional[TreeNode]) -> List[int]:
        result = []
        def traversal(root):
            if root == None:
                return
            result.append(root.val)
            traversal(root.left)
            traversal(root.right)
        traversal(root)
        return result

中序遍历的栗子leetcode 94

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

后序遍历的栗子 leetcode 145

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

三、二叉树的迭代遍历和栗子

二叉树深度优先遍历的非递归法,就是迭代法(说白了就是一套访问节点再处理节点的过程)
依旧用前序遍历举例:先让root入栈。root出栈,再先让右节点入栈,再让左节点入栈,这样栈出时就是中左右,此为一个循环。

# Definition for a binary tree node.
# 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: Optional[TreeNode]) -> List[int]:
        if root == None:
            return []
        result = []
        stack = [root]
        while stack:
            node = stack.pop()
            result.append(node.val)
            #一定要注意先判空,不能直接append
            if node.right:
                stack.append(node.right)
            if node.left:
                stack.append(node.left)
        return result

前序相对好理解,因为访问的顺序是和处理节点(放入result)的顺序是一致的,(先访问中,先处理中)

后序遍历与前序一致(同先访问中,先处理中,无非是再倒序输出),除了迭代是先入左节点,再入右节点,最后是倒序输出,代码如下:

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

但是中序有点不同,因为访问的顺序和处理的顺序不一样。此时采取的方式是用指针访问节点,用栈处理节点。so,怎么做呢?

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if root == None:
            return []
        result = []  #放结果的地方
        #不能把root先放入栈,否则会先做处理
        stack = []  # 一个处理节点的栈,存储访问过的节点
        cur = root  # 一个遍历用的指针
        while cur or stack:
            #迭代访问最左边的节点
            if cur:
                stack.append(cur)
                cur = cur.left
            #处理访问过的节点的右节点
            else:
                cur = stack.pop()
                result.append(cur.val)
                cur = cur.right
        return result

附赠:统一迭代写法

规则:访问节点和处理节点均入栈,处理的节点放入栈之后,紧接着放入一个空指针作为标记

比如[1,null,2,3],入栈顺序为[1,2 1 null ]

# Definition for a binary tree node.
# 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: Optional[TreeNode]) -> List[int]:
        if root == None:
            return []
        result = []  #放结果的地方
        stack = [root]  
        while stack:
            node = stack.pop()
            if node:
                if node.right:
                    stack.append(node.right)
                stack.append(node)
                stack.append(None)
                if node.left:
                    stack.append(node.left)

            else:
                node = stack.pop()
                result.append(node.val)
        return result
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值