【LeetCode小结】二叉树遍历

二叉树作为一个基础的数据结构,遍历算法作为一个基础的算法,两者结合当然是经典的组合了。

二叉数的遍历主要有前中后遍历和层次遍历。 前中后属于 DFS,层次遍历属于 BFS。 DFS 和 BFS 都有着自己的应用

下面主要讨论各种遍历的迭代实现方式

前序遍历

这个是遍历是比较简单的,遍历的顺序为根-左-右,可以把这种遍历方式为一个“一人吃饱全家不饿”的人。

拿到一个这个人(根节点),他首先想的是我要出栈;
出栈完了之后,在想到我的左右节点,由于栈先进后出,所以如果我有右节点则入栈,如果有左节点再继续入栈。

等到我的左节点上位(准备出栈)以后,当然继续让它自己出栈,接着在看它的左右节点。

实现如下:

    #迭代方法
    def preorderTraversal(self,root):
        res=[]
        stack=[root]
        while stack:
            node=stack.pop()
            res.append(node.val)
            if node.right!=None :stack.append(node.right)
            if node.left!=None :stack.append(node.left)   
        
        return res

中序遍历

中序遍历的顺序为左-根-右,可以看做是一个偏心与左孩子的家长

这个家长刚上位(入栈)之后,首先要考虑的是它的左孩子,要尽快让它的左孩子上位(入栈)。
左孩子上位后考虑的还是它的左孩子,所以最左侧的边界处的节点都入栈了。

如果没有左孩子,就再考虑自己,让自己出栈,然后考虑右孩子,到了右孩子之后,右孩子考虑的孩子还是它自己的左孩子。

所以中序遍历要比前序遍历要复杂一点,主要出栈以后,下一个需要考虑的是它的直接右节点node,接下来继续考虑的是node的左节点。

    def inorderTraversal(self,root):
        res=[]
        stack=[root]
        
        left=root.left
        #左节点全部先入栈——根节点最左侧节点(最左边界处的节点)
        while left!=None:
            stack.append(left)
            left=left.left
            
        #从栈中拿出节点
        #1.拿出来的节点查看还有没有右侧节点
        #2.有右侧节点的,先把他的左侧边界节点入栈
        while stack:
            node=stack.pop()
            res.append(node.val)
            
            t=node.right
            while t:
                stack.append(t)
                t=t.left
            
        return res

后序遍历

后续遍历的顺序为左-右-根,可以看做是一个无私的家长

这个家长首先考虑的是让他的左孩子和右孩子都入栈(先入栈右孩子,再入栈左孩子)。
当一个节点没有左孩子和右孩子的时候,可以开始第一次出栈,出栈以后当然要看栈中的下一个元素是否有右孩子,如果有,是否输出过了呢?
所以要设置一个变量,来记录上一个输出的是不是它的孩子(不用纠结是左孩子还是右孩子,因为父节点在栈中的位置总是比所有孩子更深),如果上一个输出的是它的孩子,说明它的孩子都已经出栈了,它就可以出栈了。

如果是叶子节点,让然可以直接出栈。

    def postorderTraversal(self,root):
        if root==None:
            return
        
        res=[]
        stack=[root]
        p=root #标识元素,用来判断节点是否“刚才”已经遍历过
        
        while stack:
            top=stack[-1]
            #该出栈元素——子节点已经遍历过了 或 为叶子节点
            if top.left==p or top.right==p or (top.left==None and top.right==None):
                p=stack.pop()
                res.append(p.val)
            #该入栈元素——先入栈右节点,在入栈当前节点左节点,是并列的
            else:
                if top.right:
                    stack.append(top.right)
                if top.left:
                    stack.append(top.left)
        return res

小结

入栈:

  • 前序:刚开始入栈一个root元素就够了,自己可以进行输出,输出后将右孩子和左孩子入栈
  • 中序:得把左侧边界节点开始入栈,并从最左节点开始出栈
  • 后序:得把当前节点的右孩子入栈和左孩子入栈,进而把左节点的右孩子和左孩子入栈,尽管开始出栈的元素和中序遍历相同,但栈中元素不同。

出栈:

  • 前序:出栈后,就在继续把栈中下一个元素出栈,接着将右、左节点入栈。
  • 中序:出栈后,遇到下一个节点,首先看这个节点的左孩子,如果有则将左孩子入栈。
  • 后序:出栈后,查看栈中下一个元素是否具有孩子节点,如果有,按照上述入栈书序进行入栈。
发布了205 篇原创文章 · 获赞 655 · 访问量 53万+

猜你喜欢

转载自blog.csdn.net/qq_33414271/article/details/98125536