プレオーダーミドルオーダーポストオーダートラバーサルについて
たとえば、上の図の通常のフルノード、A:ルートノード、B:左ノード、C:右ノード、予約注文の順序はABCです(ルートノードが最初で、次に同じレベルが最初に左、次に右)。 ;中央の順序はBAC(左から左、次にルート、最後の右)です。次の順序はBCA(最初に左、次に右、最後のルート)です。
これらは、ルートノード(現在のノード)がアクセスした場所によって名前が付けられ、すべてdfsで実装されます。
フロントオーダー:
左右ルートミドルオーダー:レフトルートライト
アフターオーダー:レフトルートミドル
実用的な再帰的なアイデアで、プレオーダー、ミドルオーダー、ポストオーダーを理解することをお勧めします。
たとえば、真ん中のシーケンス:
新しいノードに到達するたびに、考慮しなければならないことの1つは、左ルート右です。現在のノードがルートです。
最初に左を見つける必要があります。(または、現在のトラバースにアクセスするために左右をトラバースしている場合、これは一部をよりよく理解する可能性があります)
したがって、そのようなバイナリツリーの面:
プレオーダートラバーサル:ABCDEFGHK
中次走査:BDCAEHGKF
注文後のトラバーサル:DCBHKGFEA
Javaコードの実装-再帰
プロローグ:
void dfs(TreeNode root) {
visit(root);
dfs(root.left);
dfs(root.right);
}
ミドルオーダー:
void dfs(TreeNode root) {
dfs(root.left);
visit(root);
dfs(root.right);
}
シーケンス後:
void dfs(TreeNode root) {
dfs(root.left);
dfs(root.right);
visit(root);
}
具体例:LeetCode144バイナリツリープレオーダートラバーサル
トピックの詳細:バイナリツリーのルートノードルートを指定し、そのノード値のプレオーダートラバーサルを返します。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> visited = new LinkedList<>();
dfs(root,visited);
return visited;
}
public void dfs(TreeNode s, List<Integer> visited){
if(s==null){
return;
}
visited.add(s.val);
dfs(s.left,visited);
dfs(s.right,visited);
return;
}
}
後続の順序と中間の順序で同様
Javaコードの実装-反復
ここでの反復では、実際にスタックを使用して再帰のアイデアをシミュレートします。
実際、再帰はスタックへの呼び出しでもあるためです。
プロローグ
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> visited = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode s = stack.pop();
if (s == null) continue;
visited.add(s.val);
stack.push(s.right); // 先右后左,保证左子树先遍历
stack.push(s.left);
}
return visited;
}
ここではスタックが使用され、スタックは先入れ先出しです。
したがって、ここでは非再帰が使用されます。事前注文を確実にするために、現在のノードが最初に追加されます。で
左リアを積み重ね、最初の左と右の後なので、ここでは右の最初のスタックであることを確実にするために、その後。左右
ポストオーダーの場合:
プレオーダートラバーサルはルート->左->右であり、ポストオーダートラバーサルは左->右->ルートです。事前注文トラバーサルをルート->右->左に変更できます。この順序は、事後注文トラバーサルとは正反対です。
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
if (node == null) continue;
ret.add(node.val);
stack.push(node.left);
stack.push(node.right);
}
Collections.reverse(ret);
return ret;
}
中間シーケンス:
これはもう少し面倒です。各ステップで、最初に左側の子ノードが空になるまで左側の子ノードをスタックに配置し、次にアクセスしてから右側の子ノードをスタックに配置します。
ここでのロジックは次のとおりです。
最初にすべての左側をスタックにプッシュし、次に
現在のノードにアクセスしてから
、右側のノードをスタックにプッシュします(プッシュがある場合、以下のコードの右側のノードはスタックにプッシュされません。ここで、whileの上部はまだスタックから外れている必要があります。これにより、時間の複雑さが大幅に増加します。必要ありません。whileの下部と上部で使用されるノードは同じノードであるため、オーバーヘッドを増やすには、スタックしてポップする必要があります。ただし、スタックとして理解できます)
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
if (root == null) return ret;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
TreeNode node = stack.pop();
ret.add(node.val);
cur = node.right;
}
return ret;
}
}
二分木のモリストラバーサル
上記の再帰的トラバーサルを実装するには、実際にはスタックに依存する必要があります。Javaでは、仮想マシンスタックのjvmの制限により、データ量が多い場合にスタックがオーバーフローします。比較的大きいので、時々、複雑なスペースが必要になります。トラバースする方法は小さくなります。
ここでのモリストラバーサルは、O(1)空間の複雑さと引き換えに、特定の時間の複雑さが犠牲になることを意味します。
再帰では、スタックに依存して、下部の要素が上部の要素として返されるため、ここにはスタックがありません。
代わりに、巧妙な方法が採用されています。つまり、この時点で、左側のサブツリーの右端のポイントがこのポイントを指しているため、当然、スタックから上位の要素をポップする操作をシミュレートして、スタックに戻ることができます。上部要素。(これはモリスでは手がかりの設定と呼ばれます)
次に、訪問後に手がかりを削除する必要があります。これは、スタックからポップされ、訪問後に破棄された要素と同じであり、バイナリツリーの変更を回避できます。 。
次に、前のトラバーサルでは、プレオーダーはポイントごとに1回トラバースされて印刷され、ミドルオーダーは2回トラバースされて印刷され、ポストオーダーは3回トラバースされて印刷されます。モリスはこれを真似しました。
pre-intermediate post-sequenceのmorris実装では、最初の2つの実装は比較的単純です。問題は見られず、使用することもできます。post-sequenceは少し扱いにくいため、リンクリストを反転する必要があります。 。最初にここにマークを付けます。アプリケーションのシナリオは比較的多くなります。必要なときに戻って理解してください。
まず、時間を節約するためによく書かれていると思われる2つのブログを配置し
ます
。https ://blog.csdn.net/wdq347/article/details/8853371 https://blog.csdn.net/pcwl1206/article/details/96977808
二分探索木BSTの場合、順序付けられたシーケンスを取得するための順序どおりのトラバーサル
タイトルのように、
ここでのBSTの特徴は、左側のサブツリーのすべての値<=ルートノードの値<=右側のサブツリーの値です。
BST(バランス)の作成
バランスは毎回中間点を見つけることにあるので、バランスが取れていることが保証されます(左右のサブツリーの深さの差は1以下です)。
実際、私はそうではないBSTを構築したいと思います。バランスが取れていることが保証されます。間隔全体の中央の値をポイントとして選択するだけです。値で十分です。左側のサブツリーは間隔内のポイントの左側、右側のサブツリーはポイント内のポイントの右側です。間隔。このようなロジックは、ルートノードが左側のサブツリーのすべての値以上であり、右側のサブツリーのすべての値以下であることを保証し、それによってBSTを構築します。
また、値を選択するたびに中央の位置のみを考慮すると、各ラウンドで、左右のサブツリーの数の差が1を超えず、最終的にバランスが取れることを意味します。
(同時に、ここでmedium = l +(rl)/ 2を使用する目的は、オーバーフローを防ぐことです。(r + l)/ 2を使用すると、r + lがオーバーフローする可能性があり、rlがオーバーフローすることはありません)
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return dfs(nums,0,nums.length-1);
}
public TreeNode dfs(int[] nums,int l, int r){
if(l>r){
return null;
}
int medium = l+(r-l)/2;
TreeNode node = new TreeNode(nums[medium]);
node.left = dfs(nums,l,medium-1);
node.right = dfs(nums,medium+1,r);
return node;
}
}
参照
https://blog.csdn.net/qq_33243189/article/details/80222629
LeetCode