二叉树的遍历:非递归版:
注意一下所谓“左中右”的含义,这就是前序中序后序本来面目。前序中序后序,三者是以“中”先打印还是后打印区分的
- 前序遍历:中左右:当前节点(中)为头,弹栈顺序是先左再右
- 步骤
- 先把当前节点的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