二叉树的遍历

181 阅读4分钟

  二叉树的遍历可以分为 深度优先 遍历和 广度优先 遍历。其中深度优先又可以分为前序遍历、中序遍历、后序遍历;广度优先又称为层次遍历

一、深度优先

  所谓深度优先,即从根节点开始,沿着一条分支尽可能深的遍历分支上的所有节点。当一条分支上的节点遍历完成以后,回溯到父节点,对其他分支上的节点进行相同的遍历操作。

⒈ 前序遍历

根节点→左子树→右子树

① 递归实现
class Node:
    def __init__(self, item):
        self.item = item
        self.left = None
        self.right = None

    def __repr__(self):
        return str(self.__dict__)

    def __str__(self):
        return str(self.__dict__)


def pre_order(root):
    if root is None:
        return
    print(root.item)
    pre_order(root.left)
    pre_order(root.right)
⓶ 非递归实现

  通常,递归遍历容易理解,但对内存得消耗却非常厉害。所以这里介绍两种采用非递归方式实现的前序遍历

  ⅰ采用堆栈方式实现,利用堆栈先进后出的特点,先遍历根节点以及左子树,然后将当前节点存入堆栈;当左子树遍历完成后,再从堆栈中弹出最后存入的节点,如果该节点存在右子树,则遍历右子树;如果不存在右子树,则继续从堆栈中然出节点。

def pre_order_non_recur(root):
    stack = []
    node = root

    while node is not None or len(stack) > 0:
        if node is not None:
            print(node.item, sep=" ")
            stack.append(node)
            node = node.left
        else:
            node = stack.pop()
            node = node.right

   ⅱ以根节点为 current 节点    如果 current 节点的左子节点为空,则打印 current 的值,并将 current 指向其右子节点;    如果 current 节点的左子节点不为空,将其命名为 prev,如果 prev 的右子节点已经指向了 current 节点,则将 prev 的右子节点置为空,并将 current 指向 current 的游子节点;如果 prev 的右子节点为空,则打印 current 的值,并将 prev 的右子节点指向 current,将 current 指向 current 的左子节点;    循环以上操作知道 current 为空 示意图

def pre_order_non_recur_v2(root):
    current = root

    while current is not None:
        if current.left is None:
            print(current.item, end=" ")
            current = current.right
        else:
            prev = current.left
            while prev.right is not None and prev.right != current:
                prev = prev.right

            if prev.right is None:
                print(current.item, end=" ")
                prev.right = current
                current = current.left
            else:
                prev.right = None
                current = current.right

⒉ 中序遍历

左子树→根节点→右子树

① 递归实现
def in_order(root):
    if root is None:
        return
    in_order(root.left)
    print(root.item)
    in_order(root.right)
⓶ 非递归实现

  ⅰ 堆栈方式实现

def in_order_non_recur(root):
    stack = []
    node = root

    while node is not None or len(stack) > 0:
        if node is not None:
            stack.append(node)
            node = node.left
        else:
            node = stack.pop()
            print(node.item, sep=" ")
            node = node.right

   ⅱ 此方法与前序遍历的第二种非递归实现方法原理相同,此处不再赘述

def in_order_non_recur_v3(root):
    current = root

    while current is not None:
        if current.left is None:
            print(current.item, end=" ")
            current = current.right
        else:
            prev = current.left
            while prev.right is not None and prev.right != current:
                prev = prev.right

            if prev.right is None:
                prev.right = current
                current = current.left
            else:
                prev.right = None
                print(current.item, end=" ")
                current = current.right

⒊ 后序遍历

左子树→右子树→根节点

① 递归实现
def post_order(root):
    if root is None:
        return
    post_order(root.left)
    post_order(root.right)
    print(root.item)
⓶ 非递归实现

   ⅰ 使用两个堆栈实现,此种方法实现起来相对简单,理解起来也相对容易

def post_order_non_recur(root):
    stack_first = []
    stack_second = []

    stack_first.append(root)
    while len(stack_first) > 0:
        node = stack_first.pop()
        stack_second.append(node)
        if node.left is not None:
            stack_first.append(node.left)
        if node.right is not None:
            stack_first.append(node.right)

    while len(stack_second) > 0:
        node = stack_second.pop()
        print(node.item, end=" ")

   ⅱ只用一个堆栈实现

def post_order_non_recur_v2(root):
"""
将二叉树按照 右子树、根节点、左子树 的顺序入栈
然后再依次出栈,如果弹出的节点为左子树,则正常输出;如果弹出的节点为根节点,则同时弹出右子树,并将根节点重新入栈,然后对根节点的右子树重复上述操作,知道堆栈为空
"""
    stack = []

    while True:
        while root is not None:
            if root.right is not None:
                stack.append(root.right)

            stack.append(root)
            root = root.left

        root = stack.pop()

        if root.right is not None and len(stack) > 0 and stack[-1] == root.right:
            stack.pop()
            stack.append(root)
            root = root.right
        else:
            print(root.item, end=" ")
            root = None

        if len(stack) <= 0:
            break
            
            
    def post_order_non_recur_v3(root):
    """
    此方法与前述方法原理相同,不同之处在于此方法将 根节点 两次入栈,代替了前述方法中将右子树和根节点相继入栈的操作
    扎样,在后续操作中避免了将右子树出栈然后将根节点再次入栈
    """
    stack = []

    while True:
        while root is not None:
            stack.append(root)
            stack.append(root)
            root = root.left

        if len(stack) <= 0:
            break

        root = stack.pop()

        if len(stack) > 0 and stack[-1] == root:
            root = root.right
        else:
            print(root.item, end=" ")
            root = None

   ⅲ 使用集合实现

def post_order_non_recur_v4(root):
"""
从根节点开始按照 左子树→右子树的数序遍历二叉树的节点,已经打印输出的节点放入集合 visited
每打印输出一个节点,则重新从根节点开始重复上述操作
"""
    temp = root
    visited = set()

    while temp is not None and temp not in visited:
        if temp.left is not None and temp.left not in visited:
            temp = temp.left
        elif temp.right is not None and temp.right not in visited:
            temp = temp.right
        else:
            print(temp.item, end=" ")
            visited.add(temp)
            temp = root

二、 广度优先

   所谓广度优先,即从根节点开始,按照深度递增的顺序,遍历完当前深度的所有节点后再遍历下一个深度的所有节点。

   以下代码实现利用了队列先进先出的特点。

def level_order(root):
    if root is None:
        return root

    queue = []
    queue.append(root)

    while len(queue) > 0:
        root = queue.pop(0)
        print(root.item, end=" ")
        if root.left is not None:
            queue.append(root.left)
        if root.right is not None:
            queue.append(root.right)