0トピックの説明
Leetcodeの元のタイトルへのリンク:バイナリツリーの順序どおりのトラバース
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
1再帰的アルゴリズム
アイデアとアルゴリズム
まず、バイナリツリーの順序どおりのトラバースとは何かを理解する必要があります。左側のサブツリー-ルートノード-右側のサブツリーにアクセスする方法でこのツリーをトラバースし、左側のサブツリーまたは右側のサブツリーにアクセスするときは、次のようにします。ツリー全体がトラバースされるまで、同じ方法でトラバースします。したがって、トラバーサルプロセス全体は自然に再帰的であり、再帰関数を使用してこのプロセスを直接シミュレートできます。
定義inorder(root)は、現在ルートノードにトラバースされている回答を表します。定義によれば、inorder(root.left)を再帰的に呼び出してルートノードの左側のサブツリーをトラバースし、ルートノードの値を回答に追加してから、再帰的にinorderを呼び出すだけです。 (root.right)ルートノードの右側のサブツリーをトラバースするための再帰的終了の条件は、空のノードに遭遇することです。
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)
アルゴリズムの複雑さ
時間の複雑さ:O(n)O(n)O (n )、ここでnnnはバイナリツリーノードの数です。バイナリツリーのトラバーサルでは、各ノードに1回だけアクセスします。
スペースの複雑さ:O(n)O(n)O (n )。スペースの複雑さは再帰的なスタックの深さに依存し、バイナリツリーがチェーンの場合、スタックの深さはO(n)O(n)に達する可能性がありますO (n )レベル。
2反復アルゴリズム(スタック)
再帰関数を反復的に実装することもできます。2つの方法は同等です。違いは、スタックは再帰中に暗黙的に維持されるため、反復時にこのスタックを明示的にシミュレートする必要があることです。
# 其核心思想如下:
# 使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
# 如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
# 如果遇到的节点为灰色,则将节点的值输出。
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
White, Gray = 0, 1
res = []
stack = [(White,root)]
while stack:
color, node = stack.pop()
if not node: continue
if color == White:
stack.append((White,node.right))
stack.append((Gray,node))
stack.append((White,node.left))
else:
res.append(node.val)
return res
アルゴリズムの複雑さ
時間の複雑さ:O(n)O(n)O (n )、ここでnnnはバイナリツリーノードの数です。バイナリツリーのトラバーサルでは、各ノードに1回だけアクセスします。
スペースの複雑さ:O(n)O(n)O (n )。スペースの複雑さは再帰的なスタックの深さに依存し、バイナリツリーがチェーンの場合、スタックの深さはO(n)O(n)に達する可能性がありますO (n )レベル。
3モリスインオーダートラバーサル
アイデアとアルゴリズム
モリストラバーサルアルゴリズムは、バイナリツリーをトラバースするもう1つの方法であり、O(1)への非再帰的な順序トラバーサルのスペースの複雑さを軽減できます。
モリストラバーサルは一定のスペーストラバーサルメソッドであり、その本質はスレッド化されたバイナリツリー(スレッド化されたバイナリツリー)であり、本質的には、バイナリツリー内のNULLへのn +1ポインタを実際に使用します。
トラバーサルプロセス中、モリストラバーサルは、リーフノードの空の右ポインターを使用して、順序どおりのトラバーサルの後続ノードを指すため、スタックへの依存を回避します。
# (1)对于一个节点cur,找到它左子树的最右节点,看这个节点的right指针是null还是指向cur。
# (2)如果是null,说明左子树还没处理过,更新该指针为cur,然后进入左子树继续上述流程。
# (3)如果该指针已经是cur了,说明左子树已经处理完毕,现在是处理完毕后顺这最右节点的right指针回到该cur节点的,需先将该right指针恢复为null,处理该cur节点后进入右子树重复流程⑴。
class Solution:
def inorderTraversal(self, root: TreeNode) -> list:
node, res = root, []
while node:
if not node.left:
res.append(node.val)
node = node.right
else:
pre = node.left
while pre.right and pre.right != node:
pre = pre.right
if not pre.right:
pre.right = node
node = node.left
else:
pre.right = None
res.append(node.val)
node = node.right
return res
アルゴリズムの複雑さ
時間の複雑さ:O(n)O(n)O (n )、ここでnnnは、バイナリ検索ツリー内のノードの数です。モリストラバーサルでは、各ノードが2回訪問されるため、合計時間の複雑さはO(2 n)O(2n)です。O (2 n ) =O(n)O(n)O (n )。
スペースの複雑さ:O(1)O(1)O (1 )。