【力扣】DFS/BFS/迭代

内容源于 https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/python3-er-cha-shu-suo-you-bian-li-mo-ban-ji-zhi-s/

二叉树遍历系列总结
这里分别给出了三种二叉树的遍历方法与N叉树的前序遍历,及其时空复杂度
1:递归:直接递归版本、针对不同题目通用递归版本(包括前序、中序、后序)
2:迭代:最常用版本(常用主要包括前序和层序,即【DFS和BFS】)、【前中后】序遍历通用版本(一个栈的空间)、【前中后层】序通用版本(双倍栈(队列)的空间)
3:莫里斯遍历:利用线索二叉树的特性进行遍历
4:N叉树的前序遍历

LeeCode题目链接:
144. 二叉树的前序遍历
94. 二叉树的中序遍历
145. 二叉树的后序遍历
102. 二叉树的层序遍历
589. N叉树的前序遍历

  1 # Definition for a binary tree node.
  2 # class TreeNode:
  3 #     def __init__(self, x):
  4 #         self.val = x
  5 #         self.left = None
  6 #         self.right = None
  7 
  8 
  9 
 10 # 递归
 11 # 时间复杂度:O(n),n为节点数,访问每个节点恰好一次。
 12 # 空间复杂度:空间复杂度:O(h),h为树的高度。最坏情况下需要空间O(n),平均情况为O(logn)
 13 
 14 # 递归1:二叉树遍历最易理解和实现版本
 15 class Solution:
 16     def preorderTraversal(self, root: TreeNode) -> List[int]:
 17         if not root:
 18             return []
 19         # 前序递归
 20         return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)
 21         # # 中序递归 
 22         # return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
 23         # # 后序递归
 24         # return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) + [root.val]
 25 
 26 # 递归2:通用模板,可以适应不同的题目,添加参数、增加返回条件、修改进入递归条件、自定义返回值
 27 class Solution:
 28     def preorderTraversal(self, root: TreeNode) -> List[int]:
 29         def dfs(cur):
 30             if not cur:
 31                 return      
 32             # 前序递归
 33             res.append(cur.val)
 34             dfs(cur.left)
 35             dfs(cur.right) 
 36             # # 中序递归
 37             # dfs(cur.left)
 38             # res.append(cur.val)
 39             # dfs(cur.right)
 40             # # 后序递归
 41             # dfs(cur.left)
 42             # dfs(cur.right)
 43             # res.append(cur.val)      
 44         res = []
 45         dfs(root)
 46         return res
 47 
 48 
 49 
 50 # 迭代
 51 # 时间复杂度:O(n),n为节点数,访问每个节点恰好一次。
 52 # 空间复杂度:O(h),h为树的高度。取决于树的结构,最坏情况存储整棵树,即O(n)
 53 
 54 # 迭代1:前序遍历最常用模板(后序同样可以用)
 55 class Solution:
 56     def preorderTraversal(self, root: TreeNode) -> List[int]:
 57         if not root:
 58             return []        
 59         res = []
 60         stack = [root]
 61         # # 前序迭代模板:最常用的二叉树DFS迭代遍历模板
 62         while stack:
 63             cur = stack.pop()
 64             res.append(cur.val)
 65             if cur.right:
 66                 stack.append(cur.right)
 67             if cur.left:
 68                 stack.append(cur.left)
 69         return res
 70         
 71         # # 后序迭代,相同模板:将前序迭代进栈顺序稍作修改,最后得到的结果反转
 72         # while stack:
 73         #     cur = stack.pop()
 74         #     if cur.left:
 75         #         stack.append(cur.left)
 76         #     if cur.right:
 77         #         stack.append(cur.right)
 78         #     res.append(cur.val)
 79         # return res[::-1]
 80 
 81 # 迭代1:层序遍历最常用模板
 82 class Solution:
 83     def levelOrder(self, root: TreeNode) -> List[List[int]]:
 84         if not root:
 85             return []
 86         cur, res = [root], []
 87         while cur:
 88             lay, layval = [], []
 89             for node in cur:
 90                 layval.append(node.val)
 91                 if node.left: lay.append(node.left)
 92                 if node.right: lay.append(node.right)
 93             cur = lay
 94             res.append(layval)
 95         return res
 96 
 97         
 98         
 99 # 迭代2:前、中、后序遍历通用模板(只需一个栈的空间)
100 class Solution:
101     def inorderTraversal(self, root: TreeNode) -> List[int]: 
102         res = []
103         stack = []
104         cur = root
105         # 中序,模板:先用指针找到每颗子树的最左下角,然后进行进出栈操作
106         while stack or cur:
107             while cur:
108                 stack.append(cur)
109                 cur = cur.left
110             cur = stack.pop()
111             res.append(cur.val)
112             cur = cur.right
113         return res
114         
115         # # 前序,相同模板
116         # while stack or cur:
117         #     while cur:
118         #         res.append(cur.val)
119         #         stack.append(cur)
120         #         cur = cur.left
121         #     cur = stack.pop()
122         #     cur = cur.right
123         # return res
124         
125         # # 后序,相同模板
126         # while stack or cur:
127         #     while cur:
128         #         res.append(cur.val)
129         #         stack.append(cur)
130         #         cur = cur.right
131         #     cur = stack.pop()
132         #     cur = cur.left
133         # return res[::-1]
134         
135 
136 
137 # 迭代3:标记法迭代(需要双倍的空间来存储访问状态):
138 # 前、中、后、层序通用模板,只需改变进栈顺序或即可实现前后中序遍历,
139 # 而层序遍历则使用队列先进先出。0表示当前未访问,1表示已访问。
140 class Solution:
141     def preorderTraversal(self, root: TreeNode) -> List[int]:
142         res = []
143         stack = [(0, root)]
144         while stack:
145             flag, cur = stack.pop()
146             if not cur: continue
147             if flag == 0:
148                 # 前序,标记法
149                 stack.append((0, cur.right))
150                 stack.append((0, cur.left))
151                 stack.append((1, cur))
152                 
153                 # # 后序,标记法
154                 # stack.append((1, cur))
155                 # stack.append((0, cur.right))
156                 # stack.append((0, cur.left))
157                 
158                 # # 中序,标记法
159                 # stack.append((0, cur.right))
160                 # stack.append((1, cur))
161                 # stack.append((0, cur.left))  
162             else:
163                 res.append(cur.val)  
164         return res
165         
166         # # 层序,标记法
167         # res = []
168         # queue = [(0, root)]
169         # while queue:
170         #     flag, cur = queue.pop(0)  # 注意是队列,先进先出
171         #     if not cur: continue
172         #     if flag == 0:
173                   # 层序遍历这三个的顺序无所谓,因为是队列,只弹出队首元素
174         #         queue.append((1, cur))
175         #         queue.append((0, cur.left))
176         #         queue.append((0, cur.right))
177         #     else:
178         #         res.append(cur.val)
179         # return res
180 
181 
182 
183 # 莫里斯遍历
184 # 时间复杂度:O(n),n为节点数,看似超过O(n),有的节点可能要访问两次,实际分析还是O(n),具体参考大佬博客的分析。
185 # 空间复杂度:O(1),如果在遍历过程中就输出节点值,则只需常数空间就能得到中序遍历结果,空间只需两个指针。
186 # 如果将结果储存最后输出,则空间复杂度还是O(n)。
187 
188 # PS:莫里斯遍历实际上是在原有二叉树的结构基础上,构造了线索二叉树,
189 # 线索二叉树定义为:原本为空的右子节点指向了中序遍历顺序之后的那个节点,把所有原本为空的左子节点都指向了中序遍历之前的那个节点
190 # emmmm,好像大学教材学过,还考过
191 
192 # 此处只给出中序遍历,前序遍历只需修改输出顺序即可
193 # 而后序遍历,由于遍历是从根开始的,而线索二叉树是将为空的左右子节点连接到相应的顺序上,使其能够按照相应准则输出
194 # 但是后序遍历的根节点却已经没有额外的空间来标记自己下一个应该访问的节点,
195 # 所以这里需要建立一个临时节点dump,令其左孩子是root。并且还需要一个子过程,就是倒序输出某两个节点之间路径上的各个节点。
196 # 具体参考大佬博客
197 
198 # 莫里斯遍历,借助线索二叉树中序遍历(附前序遍历)
199 class Solution:
200     def inorderTraversal(self, root: TreeNode) -> List[int]:
201         res = []
202         # cur = pre = TreeNode(None)
203         cur = root
204 
205         while cur:
206             if not cur.left:
207                 res.append(cur.val)
208                 # print(cur.val)
209                 cur = cur.right
210             else:
211                 pre = cur.left
212                 while pre.right and pre.right != cur:
213                     pre = pre.right
214                 if not pre.right:
215                     # print(cur.val) 这里是前序遍历的代码,前序与中序的唯一差别,只是输出顺序不同
216                     pre.right = cur
217                     cur = cur.left
218                 else:
219                     pre.right = None
220                     res.append(cur.val)
221                     # print(cur.val)
222                     cur = cur.right
223         return res
224 
225 
226 
227 # N叉树遍历
228 # 时间复杂度:时间复杂度:O(M),其中 M 是 N 叉树中的节点个数。每个节点只会入栈和出栈各一次。
229 # 空间复杂度:O(M)。在最坏的情况下,这棵 N 叉树只有 2 层,所有第 2 层的节点都是根节点的孩子。
230 # 将根节点推出栈后,需要将这些节点都放入栈,共有 M?1个节点,因此栈的大小为 O(M)。
231 
232 
233 """
234 # Definition for a Node.
235 class Node:
236     def __init__(self, val=None, children=None):
237         self.val = val
238         self.children = children
239 """
240 
241 # N叉树简洁递归
242 class Solution:
243     def preorder(self, root: Node) -> List[int]:
244         if not root: return []
245         res = [root.val]
246         for node in root.children:
247             res.extend(self.preorder(node))
248         return res
249 
250 # N叉树通用递归模板
251 class Solution:
252     def preorder(self, root: Node) -> List[int]:
253         res = []
254         def helper(root):
255             if not root:
256                 return
257             res.append(root.val)
258             for child in root.children:
259                 helper(child)
260         helper(root)
261         return res
262 
263 # N叉树迭代方法
264 class Solution:
265     def preorder(self, root: Node) -> List[int]:
266         if not root:
267             return []
268         s = [root]
269         # s.append(root)
270         res = []
271         while s:
272             node = s.pop()
273             res.append(node.val)
274             # for child in node.children[::-1]:
275             #     s.append(child)
276             s.extend(node.children[::-1])
277         return res
278 
279 
280 作者:821218213
281 链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/python3-er-cha-shu-suo-you-bian-li-mo-ban-ji-zhi-s/
282 来源:力扣(LeetCode)

 

e

 

【力扣】DFS/BFS/迭代

上一篇:word2vec原理及实战


下一篇:使用golang实现栈(stack)