文章目录
python 用append + pop模拟栈,用append + pop(0) 模拟队列
二叉树的前中后三种遍历方式分别对应根节点输出位置,前序遍历即根节点最先输出,根左右。中序遍历为根节点在中间输出,左根右。后序遍历为根节点最后输出,左右根。
这三种遍历方式均为深度优先遍历,即其遍历方式均为遍历完树的一个分支,再遍历另一个分支,而不是先遍历完一层再遍历另一层。
深度优先遍历,为从当前节点,沿着一个方向逐步探索,当前节点的另一个方向还并未被探索。因此当前节点不能直接出结构体,其需要等待其下所有节点都被探索完,都出结构体后才能出结构体,因此深度优先遍历选用后进先出结构体“栈”。
1. 前序遍历
根左右
a. 二叉树前序遍历
https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
遍历过程为下图,蓝色为正在遍历节点,红色为已经被遍历过的节点。
1)递归法
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
# 前序遍历:根左右
''' 1. 递归法,很简单,根节点append进list,递归左节点,递归右节点
class Solution(object):
def preorder(self, root, result):
result.append(root.val)
if root.left:
self.preorder(root.left, result)
if root.right:
self.preorder(root.right, result)
return result
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
result = list()
if root is None:
return result
self.preorder(root, result)
return result
'''
2)非递归
栈顶出栈,栈顶右节点入栈,栈顶左节点入栈,重复过程,直到栈空。
蓝色为入栈节点,红色为已出栈节点。
'''
2. 迭代法 根左右
深度优先遍历,用栈
期待出栈顺序为 (即遍历顺序)
根左右
则操作的顺序为
根入栈 -- 根出栈 -- 右子节点入栈 -- 左子节点入栈 -- 左子节点出栈 -- 右子节点出栈
'''
class Solution(object):
def preorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
result = list()
if root is None:
return result
tree = list()
tree.append(root)
while len(tree) != 0:
node = tree.pop()
result.append(node.val)
if node.right:
tree.append(node.right)
if node.left:
tree.append(node.left)
return result
b. N叉树前序遍历
https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/
和二叉树没有太大区别,就是二叉树就是左右子节点,n叉树要循环所有children
1)递归法
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
''' 1. 递归法
class Solution(object):
def pre(self, root, result):
result.append(root.val)
for node in root.children:
if node is not None:
self.pre(node, result)
return
def preorder(self, root):
"""
:type root: Node
:rtype: List[int]
"""
result = list()
if root is None:
return result
self.pre(root, result)
return result
'''
2)非递归
''' 2. 迭代法 出栈顶,从children尾开始入栈,以便可以从children头开始出栈 '''
class Solution(object):
def preorder(self, root):
"""
:type root: Node
:rtype: List[int]
"""
result = list()
if root is None:
return result
tree = list()
tree.append(root)
while len(tree) != 0:
node = tree.pop()
result.append(node.val)
for i in range(1, len(node.children)+1):
tmp = node.children[-i]
if tmp is not None:
tree.append(node.children[-i])
return result
2. 中序遍历
左根右
a. 二叉树的中序遍历
https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
蓝色为正在遍历的节点,红色为已经遍历过的节点
1)递归法
# 1. 递归法 递归左节点,result append根节点,递归右节点
class Solution(object):
def inorder(self, root, result):
if root.left:
self.inorder(root.left, result)
result.append(root.val)
if root.right:
self.inorder(root.right, result)
return
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
result = list()
if root is None:
return result
self.inorder(root, result)
return result
2)非递归
蓝色为入栈节点,红色为已出栈节点
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
''' 2. 迭代法
期待出栈顺序为
左根右
则其操作顺序为
根入栈 -- (根左节点入栈 -- 根左节点左节点入栈,直到没有左节点 -- 栈顶出栈(左节点))
-- 栈顶有右节点,右节点入栈,重复前面括号中过程
-- 栈顶没有右节点,接着出栈,直到出栈元素有右节点,或栈已出空
'''
class Solution(object):
def inorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
result = list()
if root is None:
return result
tree = list()
cur = root
while len(tree) != 0 or cur is not None:
if cur is not None:
tree.append(cur)
cur = cur.left
else:
node = tree.pop()
result.append(node.val)
cur = node.right
return result
递归反而更快一些,应该是栈方法节点都需要遍历两遍的原因吧
3. 后序遍历
a. 二叉树的后序遍历
左右根
https://leetcode-cn.com/problems/binary-tree-postorder-traversal/
1)递归法
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def postorder(self, root, result):
if root is None:
return
if root.left:
self.postorder(root.left, result)
if root.right:
self.postorder(root.right, result)
result.append(root.val)
return
def postorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
result = list()
self.postorder(root, result)
return result
2)非递归
蓝色为入栈节点,红色为已出栈节点
注意后序遍历与前序和中序遍历不同,前序和中序遍历是沿着当前路径向下探寻,当发现没有新节点可以入栈,则栈顶出栈,并以栈顶为新的根节点,接着向下探寻。但是后序遍历,当当前路径探寻到尽头,栈顶元素不能直接出栈,需要判断栈顶元素是否有另一个未被遍历子节点,若存在未被遍历子节点,则该子节点入栈。当栈顶元素所有子节点被遍历完成,栈顶元素出栈。
难点在于需要判断该节点左右节点均被遍历后,该节点才能出栈。
a)标记法
节点p入栈,左节点入栈,左子树遍历完,右节点入栈,右节点遍历完,节点p出栈。则用mark标记刚刚出栈的元素,如果其为当前栈顶元素的右节点,代表当前栈顶元素左右节点均被遍历,可以出栈。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
'''
迭代法
预期出栈顺序为 左右根
操作为 根入栈 -- 左节点入栈 -- 左节点入栈,直到没有左节点
-- 栈顶元素有右节点,栈顶元素不出栈,右节点入栈
-- 栈顶元素没有右节点,则当前栈顶元素左右节点,均遍历完,当前栈顶元素出栈
用mark标记刚刚出栈的栈顶元素,如果mark为当前栈顶元素右节点,代表当前栈顶元素左右节点均被遍历,当前栈顶元素可以出栈,如果不是,入栈其右节点
'''
def postorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
result = list()
if root is None:
return result
tree = list()
mark = None
tree.append(root)
node = root.left
while len(tree) != 0:
while node:
tree.append(node)
node = node.left
mark = None
while len(tree) != 0:
node = tree.pop(-1)
if node.right == mark: # 从右子节点遍历回来,可以出栈了
result.append(node.val)
mark = node
else: # 从左子节点遍历过来,入右子节点,重复整个遍历过程
tree.append(node)
node = node.right
break
return result
b)颠倒输出
后序遍历的输出顺序为 左 – 右 – 根,根 – 右 – 左。则设定出栈顺序为 根 – 右 – 左,入栈顺序 根入栈 – 根出栈 – 左节点入栈 – 右节点入栈。之后再颠倒回来即可。
class Solution(object):
def postorderTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[int]
"""
result = list()
if root is None:
return result
tree = list()
tree.append(root)
while len(tree) != 0:
node = tree.pop()
if node.left:
tree.append(node.left)
if node.right:
tree.append(node.right)
result.append(node.val)
return result[::-1]
b. N叉树的后序遍历
1)递归法
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution(object):
def inpostorder(self, root, result):
if root is None:
return result
for node in root.children:
if node:
self.inpostorder(node, result)
result.append(root.val)
return result
def postorder(self, root):
"""
:type root: Node
:rtype: List[int]
"""
result = list()
if not root:
return result
self.inpostorder(root, result)
return result
2)非递归
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution(object):
# 颠倒输出
def postorder(self, root):
"""
:type root: Node
:rtype: List[int]
"""
result = list()
if not root:
return result
tree = list()
tree.append(root)
while len(tree) != 0:
node = tree.pop()
for tmp in node.children:
if tmp:
tree.append(tmp)
result.append(node.val)
return result[::-1]
4. 层次遍历
树的层次遍历是先遍历完当前层,再遍历下一层,属于广度优先遍历。不同于深度优先遍历,广度优先遍历为遍历完当前节点的左右子节点,再去遍历下一个节点,因此,当前节点遍历完后可出结构体,用队列来实现。
a. 二叉树层次遍历
1)二叉树层次遍历I
https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
实现过程
因为题中要求每层为一个list,append到结果中,因此需要用一个count变量,记录每层的节点数。每pop一个节点,则当前层node数减一,当count==0,代表当前层遍历完成,当前层结果level append 到result中,进行下一层遍历。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def levelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
result = list()
if root is None:
return result
tree = list()
tree.append(root)
level = list()
while len(tree) != 0:
count = len(tree) # 记录当前层节点数,遍历完当前层后,将当前层结果append到result中
while count != 0:
node = tree.pop(0)
count -= 1
if node.left:
tree.append(node.left)
if node.right:
tree.append(node.right)
level.append(node.val)
result.append(level)
level = list()
return result
2)二叉树层次遍历II
https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/
本题要求的遍历顺序为,自底向上。基本和上一题相同,只是上一题没遍历完一层为往列表尾部追加元素,本题遍历完一层,为往链表头部追加。python像list头部追加元素的函数为list.insert(0,’’)
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def levelOrderBottom(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
result = list()
if root is None:
return result
tree = list()
tree.append(root)
level = list()
while len(tree) != 0:
count = len(tree)
while count != 0:
node = tree.pop(0)
count -= 1
if node.left:
tree.append(node.left)
if node.right:
tree.append(node.right)
level.append(node.val)
result.insert(0, level) # 这里把append换成insert
level = list()
return result
3)二叉树的锯齿形层序遍历
https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
以上两道题为每层遍历都是从左向右遍历,本题为第一层为从左向右遍历,则第二层为从右向左遍历,第三层为从左向右遍历,依次替换。则为从0开始算,偶数层从左向右,奇数层从右向左。添加一个记录当前层为奇偶的标志位,偶数层每遍历一个元素,往level尾部追加,奇数层每遍历一个元素,往level头部插入,即可。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def zigzagLevelOrder(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
result = list()
if root is None:
return result
tree = list()
level = list()
tree.append(root)
flag = 0 # 标记当前层奇偶
while len(tree) != 0:
count = len(tree)
while count != 0:
node = tree.pop(0)
count -= 1
if node.left:
tree.append(node.left)
if node.right:
tree.append(node.right)
if flag: # 根据奇偶层决定是加在队列头还是队列尾
level.insert(0, node.val)
else:
level.append(node.val)
result.append(level)
level = list()
flag = 1 - flag
return result
b. N叉树层次遍历
https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/
"""
# Definition for a Node.
class Node(object):
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution(object):
def levelOrder(self, root):
"""
:type root: Node
:rtype: List[List[int]]
"""
result = list()
if root is None:
return result
tree = list()
tree.append(root)
level = list()
while len(tree) != 0:
count = len(tree)
while count != 0:
node = tree.pop(0)
count -= 1
for tmp in node.children:
tree.append(tmp)
level.append(node.val)
result.append(level)
level = list()
return result
5. 二叉树垂序遍历(hard)
https://leetcode-cn.com/problems/vertical-order-traversal-of-a-binary-tree/
给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。
对位于 (row, col) 的每个结点而言,其左右子结点分别位于 (row + 1, col - 1) 和 (row + 1, col + 1) 。树的根结点位于 (0, 0) 。
二叉树的 垂序遍历 从最左边的列开始直到最右边的列结束,按列索引每一列上的所有结点,形成一个按出现位置从上到下排序的有序列表。如果同行同列上有多个结点,则按结点的值从小到大进行排序。
返回二叉树的 垂序遍历 序列。
思路:先遍历,得到每个节点的坐标。存储结构为以坐标为key的dict。遍历完树之后,按坐标排序,对节点进行输出。感觉难度算不上hard。
class Solution(object):
def dfs(self, root, result, x, y):
if root is None:
return result
if (x, y) in result:
result[(x, y)].append(root.val)
else:
result[(x, y)] = [root.val]
if root.left: # 左子节点,坐标x-1,y+1
self.dfs(root.left, result, x-1, y+1)
if root.right: # 右子节点,坐标x+1,y+1
self.dfs(root.right, result, x+1, y+1)
return result
def verticalTraversal(self, root):
"""
:type root: TreeNode
:rtype: List[List[int]]
"""
re = list()
result = dict()
if root is None:
return result
self.dfs(root, result, 0, 0)
# 此时result中为坐标对应的val,譬如[3,9,20,null,null,15,7],
# result为 {(2, 2): [7], (-1, 1): [9], (0, 0): [3], (1, 1): [20], (0, 2): [15]}
# 按(x,y)进行排序
sorted_result = sorted(result.items(), key=lambda item: item[0])
level = list()
same_item = list()
pre_x = 'x'
for item in sorted_result:
x = item[0][0]
value = item[1]
if x != pre_x:
if pre_x != 'x': # 换列,将当前列内容append到re中
re.append(level)
level = list()
pre_x = x
for tmp in sorted(value): # 相同坐标位进行排序
level.append(tmp)
re.append(level)
return re
6. 二叉树展开为链表
https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
注意题里要求不需要返回,直接在原地进行修改
a. 暴力法
思路:由于展开后单链表为与二叉树先序遍历顺序相同,则先先序遍历二叉树,得到结果队列。再根据结果队列,重建链表树。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def dfs(self, root, result):
if root is None:
return result
result.append(root.val)
if root.left:
self.dfs(root.left, result)
if root.right:
self.dfs(root.right, result)
return result
def flatten(self, root):
"""
:type root: TreeNode
:rtype: None Do not return anything, modify root in-place instead.
"""
result = list()
if root is None:
return result
self.dfs(root, result)
node = root
for i in range(1, len(result)):
node.left = None
node.right = TreeNode(result[i])
node = node.right
return
b. 前序遍历和展开同步
思路:展开后树的结构为
则与前序非递归遍历操作相似
根入栈 – 根出栈 – 右子节点入栈 – 左子节点入栈 – 根左节点置为null – 根右节点置为当前栈顶,即刚刚入栈的左节点
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def flatten(self, root):
"""
:type root: TreeNode
:rtype: None Do not return anything, modify root in-place instead.
"""
result = list()
if root is None:
return result
tree = list()
tree.append(root)
while len(tree) != 0:
node = tree.pop()
if node.right:
tree.append(node.right)
if node.left:
tree.append(node.left)
if len(tree) != 0:
node.left = None
node.right = tree[-1]
return