关于树的遍历,前序,中序,后序,各有3种方法。
递归;迭代;莫里斯
这里进行总结:
①递归总结:
递归只需注意一点,函数调用是小括号不是中括号,preorder(root)√ preorder[root]×
前序
class Solution:
def preorderTraversal(self,root):
def preorder(node):
return [node.val]+preorder(node.left)+preorder(node.right) if node else []
return preorder[root] #递归只需注意一点,函数调用是小括号不是中括号,preorder(root)√ preorder[root]×
中序
class Solution:
def inorderTraversal(self,root):
def inorder(node):
return inorder(node.left)+[node.val] + inorder(node.right) if node else []
return inorder(root)
后序
class Solution:
def postorderTraversal(self,root):
def postorder(node):
return postorder(node.left)+postorder(node.right)+[node.val] if node else []
return postorder(root)
时间复杂度O(n)
空间复杂度(h),h是树的高度
②迭代总结
while stack or root:#这个初始条件的意思应该是指,stack只要不为空,就继续遍历;加个or root主要是为了初始化,因为刚开始stack=[],而root不为空;改成stcak=[root],or root就可以去掉了;并不行,如果root也为空[],那么stack=[[]]是不为空的,但pop出去的tmp为空,其right是不存在的;所以初始都要判断一下stack和root是否为空;
所以初始化条件就是while stack or root:请记住
前序
class Solution:
def preorderTraversal(self,root):
res =[]
stack =[]
while stack or root:#请记住
while root:
res.append(root.val)#前序在遍历左节点之前,先将值加入结果 #直接根据中序迭代的方法,将记录遍历元素的时机改为了在入栈的时候就记录,也就是将父节点计入数组的时间提前了。
stack.append(root)
root = root.left
tmp = stack.pop()
root = tmp.right
return res
中序
class Solution:
def inorderTraversal(self,root):
res =[]
stack =[]
while stack or root:#请记住
while root:
stack.append(root)#不断添加左节点
root = root.left#先不断遍历左节点
tmp = stack.pop()
res.append(tmp.val)#在左节点遍历结束,要不断遍历右节点的时候,将值加入结果
root = tmp.right
return res
后序,在前序遍历的基础上,先将right的顺序提前,left的顺序滞后,然后再反转res
class Solution:
def postorderTraversal(self,root):
res =[]
stack = []
while stack or root:
while root:
res.append(root.val)#前序遍历的标配
stack.append(root)
root = root.right#注意这里与前序的不同,将right的顺序提前
tmp = stack.pop()
root = tmp.left#将left的顺序滞后
return res[::-1]
时间复杂度O(n)
空间复杂度(h),h是树的高度
③莫里斯遍历总结
当前节点有左子节点,就让左子节点的最右节点与当前节点建立联系
前序和中序的区别在于:
当左子节点存在的时候,添加节点元素的位置从拆除多余连接的时候变成了建立连接的时候,也就是在移动cur指针之前就得记录节点,保证当前指向的节点是最先记录的,左右子树的节点要靠后,并且不能重复记录元素。
后序和前序的区别在于:
和迭代时一样,将right提前,left滞后,再反转res;即right都替换为left,left都替换为right
前序
class Solution:
def preorderTraversal(self, root: TreeNode) -> List[int]:
node, output = root, []
while node:
if not node.left:
output.append(node.val)
node = node.right
else:
predecessor = node.left
while predecessor.right and predecessor.right is not node:
predecessor = predecessor.right
if not predecessor.right:#建立连接,找到了最右节点
output.append(node.val)#前序莫里斯遍历与中序的区别就在于这里
predecessor.right = node #建立右关系,与初始节点
node = node.left #初始节点建立关系后,继续其左节点作为初始节点
else:#拆除多余连接
predecessor.right = None
node = node.right
return output
中序
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
node, output = root, []
while node:
if not node.left: #当前节点没有左子节点
output.append(node.val)
node = node.right
else: #当前节点有左子节点
predecessor = node.left
while predecessor.right and predecessor.right is not node:
predecessor = predecessor.right
if not predecessor.right:#建立联系,找到了最右节点
predecessor.right = node #建立右关系,与初始节点
node = node.left #初始节点建立关系后,继续其左节点作为初始节点
else:#拆除联系
output.append(node.val)
predecessor.right = None
node = node.right
return output
后序
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
node, output = root, []
while node:
if not node.right: #①前序是判断左子节点,后序是判断右子节点
output.append(node.val)
node = node.left
else:
predecessor = node.right #②前序是从当前节点的左子节点不断找最右;后序是从当前节点的右子节点不断找最左
while predecessor.left and predecessor.left is not node:
predecessor = predecessor.left
if not predecessor.left:#建立连接,找到了最左节点
output.append(node.val)
predecessor.left = node
node = node.right#③前序是建立初始节点node的关系后,继续其左节点作为初始节点;后序是继续其右节点作为初始节点
else:#拆除多余连接
predecessor.left = None
node = node.left
return output[::-1]
时间复杂度O(n)空间复杂度O(1)
参考链接
https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/er-cha-shu-de-zhong-xu-bian-li-by-leetcode/
https://www.cnblogs.com/sunshuyi/p/12680577.html
https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/python3-er-cha-shu-suo-you-bian-li-mo-ban-ji-zhi-s/
https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/mo-fang-di-gui-zhi-bian-yi-xing-by-sonp/ (评论python版本)
关于树的层次遍历,也做个总结:
递归和迭代2种方法,其中迭代用队列实现BFS;
2道题,正序遍历 和 逆序遍历;其实逆序遍历完全可以通过正序遍历 [::-1]实现
衍生:如果想让每一层逆序,则left和right换换位置即可,先加入right,再加入left
①递归
正序
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
levels = [] #存放结果的列表
if not root:#定义边界条件
return levels
def helper(node,level):
if len(levels)==level:#这个地方就很巧妙,只有有下一层的时候,才初始化[]
levels.append([])
levels[level].append(node.val)#将该层的值加入,然后去看该值的左,然后去看该值的右
if node.left:
helper(node.left,level+1)
if node.right:
helper(node.right,level+1)
helper(root,0)
return levels
逆序
即先返回最后一层,可以不改变原始的步骤,只需要加入levels的时候换下索引位置就可以了
class Solution:
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
levels = []
if not root:
return levels
def helper(node,level):
if len(levels)==level:
levels.insert(0,[])#每次初始化的放在前面;正序是不用管,后面append即可
levels[-(level+1)].append(node.val)#-(level+1)表示该层的索引位置
if node.left:
helper(node.left,level+1)
if node.right:
helper(node.right,level+1)
helper(root,0)
return levels
②迭代BFS
正序
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
levels = []
if not root:
return levels
level = 0
queue = deque([root])
while queue:
levels.append([])
for i in range(len(queue)):
node = queue.popleft()
levels[level].append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
level +=1
return levels
逆序
class Solution:
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
levels = []
if not root:
return levels
level = 0
queue = deque([root])
while queue:
levels.insert(0,[])#和递归一样,改这个
for i in range(len(queue)):
node = queue.popleft()
levels[-(level+1)].append(node.val)#和递归一样,改这个
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
level +=1
return levels
n叉树的遍历
递归
class Solution:
def preorder(self, root: 'Node') -> List[int]:
res = []
def helper(node):
if not node:
return []
res.append(node.val)
for child in node.children:#2叉树是递归左右子结点,n叉树是循环递归子孩子节点
helper(child)
helper(root)
return res
迭代
class Solution:
def preorder(self, root: 'Node') -> List[int]:
if not root:
return []
s = [root]
res = []
while s:
node = s.pop()
res.append(node.val)
s.extend(node.children[::-1]) #全部遍历是用中括号[],用()要让评委笑了
#node的所有子节点逆序推入栈中。例如子节点从左到右为 v1, v2, v3,那么推入栈的顺序应当为v3, v2, v1,这样就保证了下一个遍历到的节点(即node的第一个子节点 v1)出现在栈顶的位置
return res
所有树的遍历的时间复杂度O(n),空间复杂度O(n),除了莫里斯是空间复杂度O(1)