二叉树的遍历

        若以  D、L、R分别表示根结点、左子树、右子树,理论上来说,二叉树的遍历方式有六种,
但如果限定先左后右,则只有三种遍历方式:DLR(先序遍历)、LDR(中序遍历)、LRD(后续遍历)
另外,还有一种特殊的遍历方式:层次遍历

                            A
                _____/  \_____
              /                          \
            B                              C
        _/  \_                      _/  \_
      /          \                  /          \
    D              E              F              G
                      \
                        H

递归方式特别好实现,以下是三种遍历的递归实现:
def pre_order_recursion(node):
    if not node:
        return
    print(node.data, end="")
    pre_order_recursion(node.left_child)
    pre_order_recursion(node.right_child)


def in_order_recursion(node):
    if not node:
        return
    in_order_recursion(node.left_child)
    print(node.data, end="")
    in_order_recursion(node.right_child)


def post_order_recursion(node):
    if not node:
        return
    post_order_recursion(node.left_child)
    post_order_recursion(node.right_child)
    print(node.data, end="")
层次遍历:ABCDEFGH

                            A
                _____/  \_____
              /                          \
            B                              C
        _/  \_                      _/  \_
      /          \                  /          \
    D              E              F              G
                      \
                        H
        层次遍历是一层一层进行遍历。
        由于二叉树并非线性结构,倘若和线性结构一样,只依靠一个遍历指针,获取不到父结点和兄弟结点,
        如:当遍历到  B,下一个应该是  C,但是要拿到  C需要先拿到  A,从这个角度看,似乎应事先准备个变量  parent_node  存储  A,
                可如果遍历到E,如何拿到F呢?或者遍历G后,如何拿到H呢?显然,记录前置结点这种方式并不能解决问题。
        那么应该如何进行呢?单个结点有”只能看到左右孩子“的局限性,就好像人在陌生的地方,视野有限,很容易迷路,
        如何才能按照正确的路线走下去?我们需要地图或者导航。

        所以接下来是怎么设计导航的问题:
                已知:
                        1、每个结点只能看到自己和自己的两个孩子
                        2、孩子的访问顺序是从左往右

                导航的作用是,在”迷路“时提供方向,我们会在C之后失去方向,因此导航需要告诉我们下一个是D,
                那么,导航需事先把D保存好,因为在访问B时,才能获取D,所以在访问B时就应存好D
                所以导航的设计要素:
                        1、访问到某结点时,记录下该结点的左右子结点
                        2、迷路时提供下一个结点
                        3、队列结构,先进先出

        所以,我们可以设置一个队列,先把根结点A放进去,从队头中取出A,读取数据,并把两个孩子B、C放到队尾,
        这样就排好了A之后的两个顺序,接下来再从队头取出B,把B的孩子放到队尾....循环进行直至队列为空了,
        这样就完成一层一层遍历了
        代码:

def level_order(node):
    node_list = [node, ]
    while node_list:
        n = node_list.pop(0)
        print(n.data, end="")
        if n.left_child:
            node_list.append(n.left_child)
        if n.right_child:
            node_list.append(n.right_child)
二叉树常规遍历非递归实现

        非递归实现相对来说更复杂,但从层次遍历我们知道,只要建立导航就行了
                                            A
                                _____/  \_____
                              /                          \
                            B                              C
                        _/  \_                      _/  \_
                      /          \                  /          \
                    D              E              F              G
                                      \
                                        H
        先序:ABDEHCFG
                迷路的点:访问D之后,需要访问E;访问H后,需要访问C
                因此,在D之后,导航需要提供B,  H之后还需要提供A,不难看出,这是倒着提供,符合栈的数据结构,
                所以导航的设计要素:
                        1、在左子树访问完毕后,提供父结点,以此访问右子树
                        2、记录每个结点
                        3、栈数据结构
        

def pre_order_not_recursion(node):
    parent_stacks = []
    while node or parent_stacks:
        while node:
            print(node.data, end="")
            parent_stacks.append(node)
            node = node.left_child

        # 左孩子为空,从栈中拿出父结点,获取右孩子
        # 或者 右孩子也为空,从栈中拿出上上个结点
        parent_node = parent_stacks.pop()
        node = parent_node.right_child

        中序:DBEHAFCG

def in_order_not_recursion(node):
    parent_stacks = []
    while node or parent_stacks:
        while node:
            parent_stacks.append(node)
            node = node.left_child

        # 左孩子为空,从栈中获取父结点,从而获取它的右结点
        # 或 右孩子为空,从栈中获取上上个结点
        parent_node = parent_stacks.pop()
        print(parent_node.data, end="")
        node = parent_node.right_child

        
        后序:DHEBFGCA
                后序迷路的点一样,在D之后,导航需要提供B,以获得E,但因为是后序,获得E后,接下来还需要获取B,
                因此,导航需要提供两次B,这里就有第一次和第二次的区别,为了区分这个,就可以设置一个计数器,
                当第一次从导航中获取时,则获取右子树,并且不从栈中移除,第二次则正式读取数据并从栈中移除。
  

def post_order_not_recursion(node):
    parent_stacks = list()
    node_pop_history = dict()  # 记录结点从栈中 pop 的次数,
    # 若第一次 pop,则用来获取右孩子, 然后再 push 回去
    # 若第二次 pop,则说明结点的 右子树都遍历完毕,开始读取该结点数据
    while node or parent_stacks:
        while node:
            parent_stacks.append(node)
            node = node.left_child

        parent_node = parent_stacks[-1]
        if parent_node not in node_pop_history:  # 首次,获取右孩子
            node = parent_node.right_child
            node_pop_history[parent_node] = 1
        else:  # 二次,说明右子树遍历完成,读取该结点数据
            parent_stacks.pop()
            print(parent_node.data, end="")