[算法][面试]二叉树非递归形式的遍历及层级遍历

层级遍历

这里提供了三种方法:

  1. 指针标识法
  2. 对象标识法
  3. 队列计数法

从代码量和理解上来说,第三种方法是占优的。

def level_order(root) -> List[List[int]]:  # 左程云所使用的方法,略繁琐,一般不用
    if not root:
        return []
    queue, res = [root], []
    line_res, last_one, nxt_last_one = [], root, None
    while queue:
        root = queue.pop()
        line_res.append(root.v)

        if root.left:
            queue.insert(0, root.left)
            nxt_last_one = root.left
        if root.right:
            queue.insert(0, root.right)
            nxt_last_one = root.right

        if root == last_one:
            res.append(line_res)
            line_res = []
            last_one = nxt_last_one
    return res
def level_order_2(root) -> List[List[int]]:
    level_sign = "OVER"  # 每层的结束标记符,也可以new一个Object()来替代
    queue, res = [level_sign, root], [[]]  # 注意初始化条件
    while len(queue) > 1:
        root = queue.pop()
        if root == level_sign:
            res.append([])  # 添加一个新子列表用于保存新层的节点
            queue.insert(0, level_sign)  #  插入该层的结束标记符号
        elif root is not None:
            res[-1].append(root.v)  # 最新层的子列表添加、保存节点值
            queue.insert(0, root.left)
            queue.insert(0, root.right)
    return res[:-1]  # 丢弃最后一个插入的空列表(最后一层都是空节点)
def level_order_3(root) -> List[List[int]]:
    queue, res = [root], []
    while queue:
        res.append([])
        n = len(queue)
        for _ in range(n):  # 将当前队列在上一层中所有节点遍历完
            root = queue.pop()
            if root is not None:
                res[-1].append(root.v)
                queue.insert(0, root.left)
                queue.insert(0, root.right)
    return res[:-1]

锯齿遍历也可以基于层级遍历修改,即为奇数层res[-1].append(root.v),偶数层res[-1].insert(0, root.v)

非递归遍历

1. 模板方法

先上一个模板法,同时可以非递归的得到二叉树的前序和中序遍历

def pre_template(root):
    st, res = [], []
    while st or root:
        if root:  # 入栈,走向左子树
            res.append(root.v)  # 先序遍历输出
            st.append(root)
            root = root.left
        else:  # 出栈,走向它的右子树
            root = st.pop()
            res.append(root.v)  # 中序遍历输出
            root = root.right
    return res

模板法的理解和栈回溯有关,建议自己画一棵树理解,能记很久。

模板法后序遍历二叉树需要进行一些小修改:

def pos_template(root):
    st, res = [], []
    state = {}  # 访问状态表
    while st or root:
        if root:  # 入栈,走向左子树
            state[root] = "in"  # 刚入栈
            st.append(root)
            root = root.left
        else:  # 出栈,走向它的右子树
            if st[-1] in state:  # 栈顶元素第一次被访问
                state.pop(st[-1])  # 第一次回到这个点,出状态表,即标记为可出栈状态
                root = st[-1].right  # 走向右子树,但保留栈状态
            else:
                res.append(st.pop().v)  # 出栈、后续输出
                root = None  # 置为空
    return res

——主要的改动在于else: # 出栈,走向它的右子树之后的代码,利用了一个状态访问表对访问次数进行计数,当且仅当某个节点被二次访问后才退栈并被输出。请参考这篇文章中的图例进行理解。

2. 利用函数栈与根节点的性质

特别的,前序遍历还可以使用另外一种更简短的方法:

def pre_short(root):
    st, res = [root], []
    while st:
        node = st.pop()
        if node is not None:
             res.append(node.v)  # 先序输出
             st += [node.right, node.left]  # 先压右后压左
    return res

由于该方法利用了根节点和函数栈性质,因此无法适用于中序遍历,但可以修改成后序遍历的版本:

def pos_short(root):
    st, res = [root], []
    while st:
        node = st.pop()
        if node is not None:
            res.insert(0, node.v)  # 读值的时候倒着入栈
            st += [node.left, node.right]  # 正着入栈即可
    return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值