diff --git a/103. Binary Tree Zigzag Level Order Traversal.md b/103. Binary Tree Zigzag Level Order Traversal.md new file mode 100644 index 0000000..0be4874 --- /dev/null +++ b/103. Binary Tree Zigzag Level Order Traversal.md @@ -0,0 +1,229 @@ +# step 1 +階層型BFSが穏当そう。 + +DFSは流石に無理かと感じた。あるlevelで初めに一番左のノードから見て、その次のlevelでは +逆方向というのをうまくやる方法が思いつかなかった。 + +階層型でないBFSについても、ノードを入れる/取り出す順序を変える必要を感じた。 + +`nodes_in_level`, `nodes_in_next_level`ではあるレベルのノードが +左から右に並ぶように統一して解いた。 + +- time complexity: O(n) +- space complexity: O(n) +```python +from collections import deque + + +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + zigzag_traversed_values = [] + nodes_in_level = deque([root]) + start_from_left = True + while True: + nodes_in_level = deque(filter(None, nodes_in_level)) + if not nodes_in_level: + break + values_in_level = [] + nodes_in_next_level = deque() + while nodes_in_level: + if start_from_left: + node = nodes_in_level.popleft() + values_in_level.append(node.val) + nodes_in_next_level.append(node.left) + nodes_in_next_level.append(node.right) + else: + node = nodes_in_level.pop() + values_in_level.append(node.val) + nodes_in_next_level.appendleft(node.right) + nodes_in_next_level.appendleft(node.left) + zigzag_traversed_values.append(values_in_level) + nodes_in_level = nodes_in_next_level + start_from_left = not start_from_left + return zigzag_traversed_values +``` + +ループの先頭ではstart_from_leftがTrueの時はnodes_in_levelが右のノードから左のノードに、 +Falseの時はnodes_in_levelが左から右に並ぶようにした解法。(dequeを使わない解法) + +個人的にはあまり好みではない。 +```python +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + zigzag_traversed_values = [] + start_from_left = True + nodes_in_level = [root] + while True: + nodes_in_level = list(filter(None, nodes_in_level)) + if not nodes_in_level: + break + nodes_in_level.reverse() + nodes_in_next_level = [] + values_in_level = [] + for node in nodes_in_level: + values_in_level.append(node.val) + if start_from_left: + nodes_in_next_level.append(node.left) + nodes_in_next_level.append(node.right) + else: + nodes_in_next_level.append(node.right) + nodes_in_next_level.append(node.left) + nodes_in_level = nodes_in_next_level + start_from_left = not start_from_left + zigzag_traversed_values.append(values_in_level) + return zigzag_traversed_values +``` + +Noneもとりあえず入れる方法にしたが、弾くようにするとnodes_in_next_level.append()でネストが +深くなる。 +# step 2 +- https://github.com/olsen-blue/Arai60/pull/27/files + - 階層型BFS + - ノードの探索は通常のBFSと同じにしており、あるレベルでの値のリストを必要に応じて反転している +- https://github.com/colorbox/leetcode/pull/41/files + - ほぼ上と同じ +- https://github.com/goto-untrapped/Arai60/pull/51#discussion_r1749461242 + - リストに入れる時にひっくり返すか、最後にまとめてひっくり返すか + - >自分も入れるときひっくり返す方が好きですが、最後に奇数番目の列をひっくり返すのもありかなと思います + それぞれのloopでやることを1つに絞ってるので初見での読みやすさが上がってそうです +- https://github.com/goto-untrapped/Arai60/pull/51/files + - DFSによる解法あり。読みやすいかは別にして面白かった。思いつかなかったのが悔しい。 + - traverseの順番に規則があるのなら、それと今の深さの兼ね合いで返り値となるリストの作り方をうまいこと調整できる。 +- https://github.com/python/cpython/blob/main/Objects/listobject.c#L1527 + - pythonのlistのreverseメソッド + - 中でreverse_slice()を呼んでいてこれ自体はO(n) +- https://docs.python.org/3/library/functions.html#reversed + - あれば`__reversed__()`という特殊メソッドを読んでいるみたい + - https://github.com/python/cpython/blob/main/Objects/listobject.c#L4071 + - https://github.com/python/cpython/blob/main/Objects/listobject.c#L4103 + - listの`__reversed__()`は末尾のイテレータを返しているだけみたい。 + - traverseで必要になったら、後ろから調べるようにしている。 + +step 1の解法ではノードもちゃんとzigzagに進むように作っていたが、必ずしもそうする必要はない。 +そうしなかった時の方が複雑さが少ないように感じた。 + +通常のtraverseをしてから、リストをひっくり返していく +```python +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + level_ordered_values = [] + nodes_in_level = [root] + while True: + nodes_in_level = list(filter(None, nodes_in_level)) + if not nodes_in_level: + break + nodes_in_next_level = [] + values_in_level = [] + for node in nodes_in_level: + values_in_level.append(node.val) + nodes_in_next_level.append(node.left) + nodes_in_next_level.append(node.right) + nodes_in_level = nodes_in_next_level + level_ordered_values.append(values_in_level) + + start_from_left = True + zigzag_traversed_values = [] + for left_to_right_traversed_values in level_ordered_values: + if start_from_left: + zigzag_traversed_values.append(left_to_right_traversed_values) + else: + right_to_left_traversed_values = list( + reversed(left_to_right_traversed_values) + ) + zigzag_traversed_values.append(right_to_left_traversed_values) + start_from_left = not start_from_left + return zigzag_traversed_values +``` + +stackをつかったDFS +```python +from collections import deque + + +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + zigzag_traversed_values = [] + nodes_and_levels = [(root, 0)] + while nodes_and_levels: + node, level = nodes_and_levels.pop() + if node is None: + continue + while not len(zigzag_traversed_values) > level: + zigzag_traversed_values.append(deque()) + if level % 2: + zigzag_traversed_values[level].append(node.val) + else: + zigzag_traversed_values[level].appendleft(node.val) + nodes_and_levels.append((node.left, level + 1)) + nodes_and_levels.append((node.right, level + 1)) + zigzag_traversed_values = [ + list(values) for values in zigzag_traversed_values + ] + return zigzag_traversed_values +``` + +recursiveなDFS + +preorder traversalをしながら、けれど値はzigzagに集めていくというののいい関数名が思いつかなかった。 +関数名に動作であるpreorderをいれて、中の変数にzigzagを入れた。 +```python +from collections import deque + + +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + def traverse_tree_in_preorder( + node: Optional[TreeNode], + level: int + ) -> None: + if node is None: + return + nonlocal zigzag_traversed_values + while not len(zigzag_traversed_values) > level: + zigzag_traversed_values.append(deque()) + if level % 2: + zigzag_traversed_values[level].appendleft(node.val) + else: + zigzag_traversed_values[level].append(node.val) + traverse_tree_in_preorder(node.left, level + 1) + traverse_tree_in_preorder(node.right, level + 1) + + zigzag_traversed_values = [] + traverse_tree_in_preorder(root, 0) + zigzag_traversed_values = [ + list(values) for values in zigzag_traversed_values + ] + return zigzag_traversed_values +``` + +# step 3 + +```python +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + level_ordered_values = [] + nodes_in_level = [root] + while True: + nodes_in_level = list(filter(None, nodes_in_level)) + if not nodes_in_level: + break + nodes_in_next_level = [] + values_in_level = [] + for node in nodes_in_level: + values_in_level.append(node.val) + nodes_in_next_level.append(node.left) + nodes_in_next_level.append(node.right) + nodes_in_level = nodes_in_next_level + level_ordered_values.append(values_in_level) + + zigzag_traversed_values = [] + start_from_left = True + for left_to_right_values in level_ordered_values: + if start_from_left: + zigzag_traversed_values.append(left_to_right_values) + else: + right_to_left_values = list(reversed(left_to_right_values)) + zigzag_traversed_values.append(right_to_left_values) + start_from_left = not start_from_left + return zigzag_traversed_values +``` \ No newline at end of file