数据结构与算法-二叉树的遍历(含非递归)

二叉树的遍历

树的遍历是树的一种重要的运算。所谓遍历是指对树中所有结点的信息的访问,即依次对树中每个结点访问一次且仅访问一次,我们把这种对所有节点的访问称为遍历(traversal)。那么树的两种重要的遍历模式是深度优先遍历和广度优先遍历,深度优先一般用递归,广度优先一般用队列。一般情况下能用递归实现的算法大部分也能用堆栈来实现。

广度优先遍历(层次遍历)

从树的root开始,从上到下从从左到右遍历整个树的节点

class Node(object):
    """节点类"""
    def __init__(self, elem=-1, lchild=None, rchild=None):
        self.elem = elem
        self.lchild = lchild
        self.rchild = rchild

class Tree(object):
    """树类"""
    def __init__(self, root=None):
        self.root = root

    def add(self, elem):
        """为树添加节点"""
        node = Node(elem)
        #如果树是空的,则对根节点赋值
        if self.root == None:
            self.root = node
        else:
            queue = []
            queue.append(self.root)
            #对已有的节点进行层次遍历
            while queue:
                #弹出队列的第一个元素
                cur = queue.pop(0)
                if cur.lchild == None:
                    cur.lchild = node
                    return
                elif cur.rchild == None:
                    cur.rchild = node
                    return
                else:
                    #如果左右子树都不为空,加入队列继续判断
                    queue.append(cur.lchild)
                    queue.append(cur.rchild)

    def breadth_travel(self, root):
        """利用队列实现树的层次遍历"""
        if root == None:
            return
        queue = []
        queue.append(root)
        while queue:
            node = queue.pop(0)
            print node.elem,
            if node.lchild != None:
                queue.append(node.lchild)
            if node.rchild != None:
                queue.append(node.rchild)

深度优先遍历

对于一颗二叉树,深度优先搜索是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
那么深度遍历有重要的三种方法。这三种方式常被用于访问树的节点,它们之间的不同在于访问每个节点的次序不同。这三种遍历分别叫做先序遍历(preorder),中序遍历(inorder)和后序遍历(postorder)

  • 先序遍历 在先序遍历中,我们先访问根节点,然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树
    根节点->左子树->右子树
  • 中序遍历 在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树
    左子树->根节点->右子树
  • 后序遍历 在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点
    左子树->右子树->根节点

简单的来说就像下图一样

上图所示:

先序遍历的顺序为:0,1,2

中序遍历的顺序为:1,0,2

后序遍历的顺序为:1,2,0

那么结合代码来看一下,就非常清楚了

class Node(object):
    """节点类"""
    def __init__(self, elem=-1, lchild=None, rchild=None):
        self.elem = elem
        self.lchild = lchild
        self.rchild = rchild

class Tree(object):
    """树类"""
    def __init__(self, root=None):
        self.root = root

    def add(self, elem):
        """为树添加节点"""
        node = Node(elem)
        #如果树是空的,则对根节点赋值
        if self.root == None:
            self.root = node
        else:
            queue = []
            queue.append(self.root)
            #对已有的节点进行层次遍历
            while queue:
                #弹出队列的第一个元素
                cur = queue.pop(0)
                if cur.lchild == None:
                    cur.lchild = node
                    return
                elif cur.rchild == None:
                    cur.rchild = node
                    return
                else:
                    #如果左右子树都不为空,加入队列继续判断
                    queue.append(cur.lchild)
                    queue.append(cur.rchild)

    def breadth_travel(self, root):
        """利用队列实现树的层次遍历"""
        if root == None:
            return
        queue = []
        queue.append(root)
        while queue:
            node = queue.pop(0)
            print node.elem,
            if node.lchild != None:
                queue.append(node.lchild)
            if node.rchild != None:
                queue.append(node.rchild)

    def preorder(self, root):
        """递归实现先序遍历"""
        if root == None:
            return
        print root.elem
        self.preorder(root.lchild)
        self.preorder(root.rchild)
    
    def inorder(self, root):
        """递归实现中序遍历"""
        if root == None:
            return
        self.inorder(root.lchild)
        print root.elem
        self.inorder(root.rchild)

    def postorder(self, root):
        """递归实现后续遍历"""
        if root == None:
            return
        self.postorder(root.lchild)
        self.postorder(root.rchild)
        print root.elem

    

上面是递归的写法,那下面我们来讨论一下非递归又该怎么去实现二叉树的前中后序的遍历呢?

重点:非递归遍历二叉树

三种方法都很巧妙

详细看代码示例

class Node(object):
    """二叉树节点"""
    def __init__(self, item):
        self.elem = item
        self.lchild = None
        self.rchild = None


class Tree(object):
    """二叉树"""
    def __init__(self):
        self.root = None

    def add(self, item):
        node = Node(item)
        if self.root is None:
            self.root = node
            return
        queue = [self.root]
        while queue:
            cur_node = queue.pop(0)
            if cur_node.lchild is None:
                cur_node.lchild = node
                return
            else:
                queue.append(cur_node.lchild)
            if cur_node.rchild is None:
                cur_node.rchild = node
                return
            else:
                queue.append(cur_node.rchild)


    def breadth_travel(self):
        """广度遍历(层次遍历)"""
        if self.root is None:
            return
        queue = [self.root]
        while queue:
            cur_node = queue.pop(0)
            print(cur_node.elem, end=" ")
            if cur_node.lchild is not None:
                queue.append(cur_node.lchild)
            if cur_node.rchild is not None:
                queue.append(cur_node.rchild)

    def preorder(self, node):
        """先序遍历"""
        if node is None:
            return
        print(node.elem,end=" ")
        self.preorder(node.lchild)
        self.preorder(node.rchild)

    def inorder(self, node):
        """中序遍历"""
        if node is None:
            return
        self.inorder(node.lchild)
        print(node.elem, end=" ")
        self.inorder(node.rchild)

    def postorder(self, node):
        """后序遍历"""
        if node is None:
            return
        self.postorder(node.lchild)
        self.postorder(node.rchild)
        print(node.elem, end=" ")

    def preOrderUnRecur(self,node):
        """
            ## 先序遍历非递归方式实现
            原理是利用一个栈,先压入根节点,然后当栈不为空的时候,循环执行以下过程:
            1. 先从栈中弹出一个元素,并打印
            2. 然后判断右子树为不为空,不为空,将右子树压入栈中
            3. 然后判断左子树为不为空,不为空,将左子树压入栈中
            因为栈是先进后出的 所以是 先右子树进栈,再左子树,即 (右,左) 的顺序。那么出栈的时候就是,先打印中,然后出左,再出右,一直循环,从而实现先序遍历(中左右)
        """
        if node is None:
            return
        stack = [] #定义一个栈
        stack.append(node)
        while len(stack):
            pop_node = stack.pop()
            print(pop_node.elem, end=" ")
            if pop_node.rchild is not None:
                stack.append(pop_node.rchild)
            if pop_node.lchild is not None:
                stack.append(pop_node.lchild)

        print("")

    def inOrderUnRecur(self, node):
        """
            ## 中序遍历非递归方式实现
        """
        if node is None:
            return
        stack = [] #定义一个栈
        while len(stack) or node is not None: #当根节点不为空或者栈不为空的时候进入

            if node is not None: #当前节点不为空,当前节点入栈,当前节点往左子树移动 导致一次就压入左边的一整个边线 所以 出栈的时候 是左中右
                stack.append(node)
                node = node.lchild
            else: # 当前节点为空,从栈中拿出一个,打印,当前节点往右子树移动
                node = stack.pop()
                print(node.elem,end=" ")
                node = node.rchild

        print("")

    def posOrderUnRecur(self, node):
            """
                ## 后序遍历非递归方式实现
                原理是利用两个栈,先压入根节点,然后当栈不为空的时候,循环执行以下过程:
                1. 先从第一个栈中弹出一个元素,压入第二个栈(中)
                2. 然后判断左子树为不为空,不为空,将左子树压入栈中
                3. 然后判断右子树为不为空,不为空,将右子树压入栈中
                先压左 ,再压右 后面出来就是右,左 ,压入到第二个栈就是 (左,右)
                从而,第二个栈的栈里存的是中右左,出栈就是(左右中) 实现后序遍历
            """
            if node is None:
                return
            stack1 = []  # 定义第一个栈
            stack2 = []  # 定义第二个栈
            stack1.append(node)
            while len(stack1):
                pop_node = stack1.pop()
                stack2.append(pop_node) # 参照先序遍历的时候是拿出来打印的,现在压到另一个栈中, 压入第二个栈的是(中)
                if pop_node.lchild is not None: #然后先压左 ,再压右 后面出来就是右,左 ,压入到第二个栈就是 (左,右)
                    stack1.append(pop_node.lchild)
                if pop_node.rchild is not None:
                    stack1.append(pop_node.rchild)

            while len(stack2):
                pop2_node = stack2.pop()
                print(pop2_node.elem, end=" ")

            print("")

if __name__ == '__main__':
    tree = Tree()
    tree.add(1)
    tree.add(2)
    tree.add(3)
    tree.add(4)
    tree.add(5)
    tree.add(6)
    tree.add(7)
    tree.breadth_travel()
    print(" ")
    tree.preorder(tree.root) # 递归方式: 1 2 4 5 3 6 7
    print(" ")
    tree.preOrderUnRecur(tree.root) # 非递归方式: 1 2 4 5 3 6 7

    tree.inorder(tree.root) # 递归方式: 4 2 5 1 6 3 7
    print(" ")
    tree.inOrderUnRecur(tree.root) # 非递归方式: 4 2 5 1 6 3 7


    tree.postorder(tree.root) #递归方式: 4 5 2 6 7 3 1
    print(" ")
    tree.posOrderUnRecur(tree.root) # 非pos递归方式: 4 5 2 6 7 3 1

至此,二叉树的遍历就讲完全了,可以轻松解决面试题如:

题:实现二叉树的先序、中序、后序遍历,包括递归方式和非递归方式

猜你喜欢

转载自blog.csdn.net/LELELED/article/details/86589699
今日推荐