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

层级遍历

这里提供了三种方法:

  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([])
        for _ in range(len(queue)):  # 将当前队列在上一层中所有节点遍历完
            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 not node:
            continue
        res.append(node.v)  # 先序输出
        st.append(node.right)  # 先压右后压左
        st.append(node.left)
    return res

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

def pos_short(root):
    st, res = [root], []
    while st:
        node = st.pop()
        if not node:
            continue
        st.append(node.left)  # 此时先压左后压右
        res.insert(0, node.v)  # 后序输出,注意此时向队列首部添加新元素
        st.append(node.right)
    return res

你可能感兴趣的:(算法,面试,二叉树,数据结构,算法,python)