二分木のさまざまな走査
再帰的走査
ミドルオーダートラバーサル:ミドルオーダートラバーサルの順序は左ルート右であり、再帰的な書き込み方法は比較的単純です。テンプレートを直接貼り付けます
public void inorder(TreeNode root){
if(root == null) return;
if(root.left != null) inorder(root.left);
//当前位置即为操作,可以根据题意进行添加
if(root.right != null) inorder(root.right);
}
たとえば、lettcode94はインオーダートラバーサルメソッドを使用してバイナリツリーをトラバースしますが、リストを戻す必要がある場合は、上記のコードコメントにロジックのこの部分を追加できます。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
inorder(root, res);
return res;
}
public void inorder(TreeNode root, List<Integer> res){
if(root == null) return;
if(root.left != null) inorder(root.left, res);
res.add(root.val);
if(root.right != null) inorder(root.right, res);
}
次に、他の2つを直接引用し、プレオーダートラバーサルとポストオーダートラバーサルの記述を書き出します。操作のロジックコードを異なる位置に配置するだけで済みます。
//先序遍历
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
inorder(root, res);
return res;
}
public void preorder(TreeNode root, List<Integer> res){
if(root == null) return;
res.add(root.val);
if(root.left != null) inorder(root.left, res);
if(root.right != null) inorder(root.right, res);
}
//后序遍历
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
inorder(root, res);
return res;
}
public void postorder(TreeNode root, List<Integer> res){
if(root == null) return;
if(root.left != null) inorder(root.left, res);
if(root.right != null) inorder(root.right, res);
res.add(root.val);
}
非再帰的トラバーサル
再帰的トラバーサルの利点は、コードが簡潔であるということですが、バイナリツリーにノードが多い場合、StackOverFlowをバーストしやすいため、通常、バイナリツリーの非再帰的書き込みも記述します。一般的に、再帰的書き込みは次のことができます。非再帰的書き込みに変更されます。これには、データ構造スタックを使用する必要があります。スタックは、first-in-last-outによって特徴付けられます。順序付きトラバーサルの場合、左側のノードが最初にトラバースされるため、すべての左側のノードがトラバースされます。最初にスタックに追加し、次に最後の左側のノードを追加して値を取得し、次に右側のノードを取り出して繰り返します。
public List<Integer> inorder(TreeNode root){
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
while(root != null && !stack.isEmpty()){
while(root != null){
stack.push(root);
root = root.left;
}
root = stack.pop();
res.add(root.val);
root = root.right;
}
return res;
}
中次走査を基盤として、一次走査を記述することで直接書き込むことができます。
public List<Integer> perorder(TreeNode root){
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
while(root != null || !stack.isEmpty()){
while(root != null){
res.add(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop();
root = root.right;
}
return res;
}
注文後のトラバーサルはどうですか?ポストオーダートラバーサルを実行するときは、現在のノードに左右のノードがあるかどうかを判断する必要がありますが、これはより面倒です。1次トラバーサルは左右のルートであり、ポストであるため、ここではより柔軟な方法を使用します。 -順序トラバーサルは、元のバイナリツリーの左右のルートと左右の子です。ツリーが逆になり、事前順序トラバーサルの結果を逆にして、事後トラバーサルの結果を取得できます。例えば
元の木:
1
/ \
2 3
/ \ / \
7 6 4 5
注文後のトラバーサル:7 6 2 4 5 3 1
左右のノードを反転します
1
/ \
3 2
/ \ / \
5 4 6 7
事前注文トラバーサル:1 3 5 4 2 6 7
事前順序トラバーサルを逆にすると、次のようになります。76 2 4 5 3 1は元のツリーの事後トラバーサルであり、左右のサブツリーを逆にするのは比較的簡単で実行できます。必要なのは右サブツリーのみです。最初にプレオーダートラバーサル中に、次に左側のサブツリーをトラバースします。
public List<Integer> postorder(TreeNode root){
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
while(root != null || !stack.isEmpty()){
while(root != null){
res.add(root.val);
stack.push(root);
root = root.right;
}
root = stack.pop();
root = root.left;
}
Collections.reverse(res);
return res;
}
レベルトラバーサル
階層的トラバーサルとは、上から下、左から右の順序を指し、次にBFD、幅優先アルゴリズムを一般的に使用できます。このアルゴリズムの実装には、データ構造キューの助けが必要です。キューの利点は最初です。 -イン、ファーストアウト。
なぜキューを使用するのですか?
3
/ \
9 20
/ \
15 7
戻り値:
[3,9,20,15,7]
上記の例によると、ノードの可能な左と右のサブツリーノードを取得するには、各ノードを保存する必要があることがわかります。印刷が左から右の場合、先入れ先出しでキューを使用すると、これが満たされます。特徴
public int[] levelOrder(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> q = new LinkedList<>();
if(root == null) return new int[]{
};
q.add(root);
while(!q.isEmpty()){
TreeNode temp = q.poll();
res.add(temp.val);
if(temp.left != null) q.add(temp.left);
if(temp.right != null) q.add(temp.right);
}
int[] r = new int[res.size()];
for(int i = 0;i < res.size();i++){
r[i] = res.get(i);
}
return r;
}
スタックおよびキューとしてのDequeのいくつかの一般的な方法の簡単なレビューもあります
作为栈
方法 | 効果 |
---|---|
push(e) | ポップイン |
ポップ() | 現れる |
ピーク() | スタックの一番上の要素を取ります |
作为队列
方法 | 効果 |
---|---|
オファー(e) | チームのトップに参加する |
poll() | 行末から除外する |
ピーク() | 行末から |
運動
剣はoffer32質問1を指します
二分木は上から下にレイヤーで印刷され、同じレイヤーのノードは左から右に順番に印刷され、各レイヤーは1行に印刷されます。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
レベルトラバーサルとは異なり、この問題では、各レイヤーのデータを行として出力する必要があります。これは、各行のデータがキューに格納されるため、各サイクル中に現在のキューでの操作が必要になるためです。最初にキューのサイズを覚えてから、子ノードを追加します
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
Deque<TreeNode> q = new LinkedList<>();
if(root == null) return new ArrayList<List<Integer>>();
q.offer(root);
while(!q.isEmpty()){
List<Integer> tempA = new ArrayList<>();
//这行代码很重要,需要记住当前层的队列数量
// for(int i = queue.size(); i > 0; i--)
int size = q.size();
for(int i = 0;i < size;i++){
TreeNode temp = q.poll();
if(temp.left != null) q.offer(temp.left);
if(temp.right != null) q.offer(temp.right);
tempA.add(temp.val);
}
res.add(tempA);
}
return res;
}
剣はoffer32質問2を指します
二分木をジグザグに印刷する機能を実装してください。つまり、最初の行は左から右に印刷され、2番目のレイヤーは右から左に印刷され、3番目の行は左から右に印刷されます。オン。
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[20,9],
[15,7]
]
これは上記の問題の変形であり、偶数行に遭遇した場合にのみデータを反転する必要があります
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
Deque<TreeNode> q = new LinkedList<>();
if(root == null) return new ArrayList<>();
int flag = 1;
q.offer(root);
while(!q.isEmpty()){
List<Integer> temp = new ArrayList<>();
for(int i = q.size();i >0 ;i--){
TreeNode node = q.poll();
temp.add(node.val);
if(node.left != null) q.offer(node.left);
if(node.right != null) q.offer(node.right);
}
if(flag % 2 == 0){
Collections.reverse(temp);
}
res.add(temp);
flag++;
}
return res;
}