【python数据结构与算法】二叉树的前序、中序、后序遍历(非递归)以及二叉树的层次遍历

二叉树的遍历:非递归版:

注意一下所谓“左中右”的含义,这就是前序中序后序本来面目。前序中序后序,三者是以“中”先打印还是后打印区分的

  • 前序遍历:中左右:当前节点(中)为头,弹栈顺序是先左再右
    • 步骤
      • 先把当前节点的left和right按照先右后左的次序压栈。遇到节点为None,则跳过。
      • while内:后面继续压栈的节点,是刚弹出的那个节点的左右节点。原因?“中左右”,指的是该节点先中,再该节点的左右。所以我们以弹出的节点为中,自然要获取这个节点的左右子节
    • 前序遍历题目:LeetCode144
    • code见下
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if root is None:
            return []
        res = []           # LeetCode的要求非打印,而是返回一list,因而又准备了一个空list:res
        stack = []         # 思路中提及的“辅助栈”
        stack.append(root)
        while stack:
            cur = stack.pop()
            res.append(cur.val)
            if cur.right:
                stack.append(cur.right)
            if cur.left:
                stack.append(cur.left)
        return res
  • 中序遍历:左中右
    • 步骤:
      • 指针指向当根节点。while循环判断条件为:stack不为空or指针cur指向的节点不为空
      • 当前节点is not None,当前节点压栈,指针指向该节点的left节点node = node.left;
      • 当前节点is None,从栈顶弹栈一个元素,然后指针指向弹出节点的right节点node = node_pop.right
      • 当前节点和栈一起为空,此时结束,二叉树中序遍历完毕
    • 我们不妨来聊聊为何中序遍历要这样做:
      • 由于中序遍历为“左中右”,所以当该节点非None时,说明它含有左孩子节点(只不过这个左孩子节点可能是None),所以按照“有左先左”的理念,自然指针应该先跳到这个左孩子身上;
      • 但当该节点为None的时候,说明“左”这条路已经到头,下面该去找“中”了。而中其实就是当前空节点的父节点,也就是前一次刚压入栈的那个节点,所以要先弹栈一次;现在,当前指针指向“中”(父节点),“左中”都已搞定,当然指正该指向这个“中”(父节点)的右孩子了,因而指针再跳到弹出节点的右孩子上
    • 中序遍历题目:LeetCode94
    • code见下:
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if root is not None:
            res,stack = [],[]
            cur = root
            while stack or cur:
                if cur:               # 当前节点非空,压入该节点,并将指针指向其左孩子节点
                    stack.append(cur)
                    cur = cur.left
                else:                 # 当前节点空,弹栈一次,然后将指针指向弹出节点的右孩子节点
                    cur = stack.pop()
                    res.append(cur.val)
                    cur = cur.right
            return res
        else:
            return []
  • 后序遍历:左右中:再新建一个stack(双栈!),然后按照前序遍历相反的压栈方式压栈,最后弹栈
    • 注意观察,后序遍历“左右中”和前序遍历“中左右”差别。前序遍历实现“中左右”是,先打印“中”,然后按照“右左”的顺序压栈再弹栈,最终的顺序就是“中左右”;
    • 而后序遍历,额外申请一个栈,也就是双栈,第二个栈专门用于调整打印的次序。将“中”最先压栈1,然后进入while循环,按照“左右”顺序压栈1。最终一并弹栈1存入栈2,所以顺序就会成为:“左右中”
    • 后序遍历题目:LeetCode145
    • code见下
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if root is None:
            return []
        res = []              # 栈2
        stack = []            # 栈1
        stack.append(root)    # 当前节点“中”最先压栈2
        while stack:
            cur = stack.pop()   
            res.append(cur)
# 当前节点的孩子节点按“左右”次序压栈1。最终按“先右后左”顺序弹栈并压进栈2,致使栈2从上到下的顺序为“左、右、中”,这也正是后序遍历打印顺序
            if cur.left:        
                stack.append(cur.left)
            if cur.right:
                stack.append(cur.right)
        final = []             # 返回答案用的空list
        while res:
            final.append(res.pop().val)
        return final
  • 二叉树的层次遍历:
    • 题目描述:给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
    • 思路:仍然按照顺序从头结点开始对每个节点遍历。不过这次用的不是stack,而是Queue。原因就是,因为要一层一层从左到右这么来,就要用到队列的性质——先进者先出。left节点先入队,则left节点先出队,去寻找left节点的子节点。
    • LeetCode上题目:102.二叉树的层次遍历
    • code:
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if root is None:
            return []
        res,queue = [],[]         # res是最终要返回的二维数组;queue是辅助队列
        queue.append(root)   
        res.append([root.val])
        cnt = 1
        while queue:
            level = []           # level装的是同一层二叉树所有节点的左右孩子节点的val值
            nodes = []           # 通过cnt计数,将每一层所有的节点存入nodes中去
            for i in range(cnt): # 一次性让所有同层的二叉树节点出队,这样才能一起拿到它们的所有孩子节点
                nodes.append(queue.pop(0))
            cnt = 0
            for cur in nodes:    # 对于每个节点而言,若有子节点,保存子节点val属性,然后将该子节点入队
                if cur.left:   
                    level.append(cur.left.val)
                    queue.append(cur.left)
                    cnt += 1
                if cur.right:
                    level.append(cur.right.val)
                    queue.append(cur.right)
                    cnt += 1
            if level:           # 到了二叉树的最后一层(全是None),所有节点均为空,也就都没有val属性,因而是空list。本题这种空list不加入res二维数组中
                res.append(level)
        return res
            

猜你喜欢

转载自blog.csdn.net/weixin_41712499/article/details/85858838