目次
1. 二分木の格納方法
二分木は、チェーンまたはシーケンシャルに格納できます。
次に、リンクされたストレージ メソッドはポインターを使用し、シーケンシャル ストレージ メソッドは配列を使用します。
名前が示すように、連続して格納された要素はメモリ内に連続的に分散されますが、連結ストレージはポインターを使用して、さまざまなアドレスに散在するノードを直列に接続します。
チェーン ストレージを次の図に示します。
連鎖収納は誰もが慣れ親しんだ方法なので、順番に収納方法を見ていきましょう。
実際、配列は二分木を格納するために使用され、順次格納方法が図に示されています。
バイナリ ツリー トラバーサルを格納するために配列を使用する方法は?
親ノードの配列添え字が i の場合、その左の子は i * 2 + 1 であり、右の子は i * 2 + 2 です。
しかし、チェーンで表現された二分木の方が理解しやすいので、通常はチェーンを使用して二分木を格納します。
したがって、二分木は配列で表現できることを誰もが理解する必要があります。
第二に、二分木の走査
あるルールに従って合意が成立した場合、同じツリーに対する全員のトラバーサル結果は同じでなければなりません。N がルート ノードを表し、L がルート ノードの左側のサブツリーを表し、R がルート ノードの右側のサブツリーを表す場合、ルート ノードをトラバースする順序に従って、次のトラバーサル メソッドがあります。
NLR: Preorder traversal (Preorder Traversal は preorder traversal とも呼ばれます) - ルート ノード ---> ルートの左側のサブツリー ---> ルートの右側のサブツリーにアクセスします。LNR: Inorder Traversal (Inorder Traversal) - ルートの左側のサブツリー ---> ルート ノード ---> ルートの右側のサブツリー。LRN: postorder traversal (Postorder Traversal) - ルートの左サブツリー ---> ルートの右サブツリー ---> ルート ノード
レイヤー順序トラバーサル: 二分木のルート ノードから始めて、最初のレイヤーのノードにアクセスし、次に 2 番目のレイヤーのノードに左から右、上から下、というようにアクセスします。
上記のツリーを例として、バイナリ ツリーのトラバーサル実装を簡単に紹介します。
-
事前注文トラバーサル
ルート ノード - 左のサブツリー - 右のサブツリー
予約注文トラバーサルの結果は次のとおりです。 A B D E H C F G
//前序遍历
public void preOrder(TreeNode root){
if(root == null){
return;
}
System.out.println(root.val+" ");
preOrder(root.left);
preOrder(root.right);
}
-
順序通りのトラバーサル
左のサブツリー - ルート ノード - 右のサブツリー
順不同のトラバーサルの結果は次のとおりです。 DBE HAFCG
// 中序遍历
public void inOrder(TreeNode root){
if(root == null){
return;
}
inOrder(root.left);
System.out.println(root.val+" ");
inOrder(root.right);
}
-
ポスト オーダー トラバーサル
左のサブツリー - 右のサブツリー - ルート ノード
// 后序遍历
public void postOrde(TreeNode root){
if(root == null){
return;
}
postOrde(root.left);
postOrde(root.right);
System.out.println(root.val+" ");
}
-
シーケンストラバーサル
ルート ノードから開始し、左から右へ、というように
シーケンストラバーサルの結果: A B C D E F G H
バイナリ ツリーのポスト オーダー トラバーサルの特徴: 最後のノードはルート ノードでなければなりません。
バイナリ ツリーの事前順トラバーサルは固有です。最初のノードはルート ノードでなければなりません。
-
リストを使用して、事前注文トラバーサルのノード要素を格納します
public List<Character> preOrder2(TreeNode root){
List<Character> ret = new ArrayList<>();
if(root == null){
return ret;
}
ret.add(root.val);
List<Character> leftTree = preOrder2(root.left);
ret.addAll(leftTree);
List<Character> rightTree = preOrder2(root.right);
ret.addAll(rightTree);
return ret;
}
3. 二分木のその他の操作
-
ツリーのノード数を取得する
ツリー内のノードの数を取得するには、トラバーサルの方法を使用するか、サブ問題のアイデアを使用して、再帰的な方法、つまり、左のサブツリー + 右のサブツリー + ルート ノード (+ 1) 達成する
size(root.left) +size(root.right)+1;
/**
* 遍历方法
*/
public static int nodeSize = 0;
public int size(TreeNode root){
if(root == null){
return 0;
}
nodeSize++;
size(root.left);
size(root.left);
return nodeSize;
}
/**
* 子树相加再加上根节点
* @param root
* @return
*/
public int size2(TreeNode root){
if(root == null){
return 0;
}
int tmp = size(root.left) +size(root.right)+1;
return tmp;
}
-
ツリー内のリーフ ノードの数を取得します
ツリー内のリーフ ノードの数を取得します。つまり、左のサブツリーと右のサブツリーの両方が空の場合、root.left==null&&root.right==null、左のサブツリーのリーフ ノード + 上のすべてのリーフ ノード右のサブツリーまたは一時変数 leafSize++ を使用します。
/**
* 获取叶子节点的个数
* @param root
* @return
*/
//子问题思路
public int getLeafNodeCount(TreeNode root){
if(root == null){
return 0;
}
if(root.left==null&&root.right==null){
return 1;
}
int tmp = getLeafNodeCount(root.left)+getLeafNodeCount(root.right);
return tmp;
}
//遍历方法
public static int leafSize = 0;
public int getLeafNodeCount2(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null&&root.right == null){
leafSize++;
}
getLeafNodeCount2(root.left);
getLeafNodeCount2(root.right);
return leafSize;
}
-
k 層ノードの数を取得する
k 番目の層のノード数を求めると、左の木の k-1 層 + 右の木の k-1 層に変換できます。
K=3 のとき、左の木の 2 層目と右の木の 2 層目のノード数に変換し、最後に k = 1 のときに変換して、左から 2 層目のノード 2 と、右から 2 番目の層 3 番目の層のノードの数は、ノード 2 を追加すると 4 になります。
public int getKLevelNodeCount(TreeNode root,int k){
if(root == null|| k <= 0){
return 0;
}
if(k==1){
return 1;
}
int tmp = getKLevelNodeCount(root.left,k-1)+getKLevelNodeCount(root.right,k-1);
return tmp;
}
-
二分木の深さを取得する
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
//左右子树的最大c
return leftHeight > rightHeight ? leftHeight+1 : rightHeight +1;
}