ツリー構造と概念
ツリー構造
ツリーの定義:ツリーは非線形データ構造であり、n (n>=0) 個の有限ノードで構成される階層関係のセットです。根が上を向き、葉が下を向いている、逆さまの木に見えることからツリーと呼ばれています。
ツリーの構造には次の定義があります。
ルート ノードと呼ばれる特別なノードがあり、ルート ノードには先行ノードがありません。
ルートノードを除く他のノードは互いに素なM(M>0)個の集合T 1 , T 2 , ..., T m T_1, T_2,..., T_mに分割される。T1、て2、。。。。。。、てメートル、ここで、それぞれはT i T_iを設定しますT私は(1<= i <= m) は、ツリーに似た構造を持つ別のサブツリーです。各サブツリーのルート ノードには先行ノードが 1 つだけあり、0 個以上の後続ノードを持つことができます。
ツリーは再帰的に定義されます。
ツリー構造では、サブツリー間に交差があってはなりません。そうでない場合、それはツリー構造ではありません。
どのツリーもルートとサブツリーに分割され、ツリーの各ルート ノードがそのサブツリーになります。
たとえば、下の右の図では、A がルート ノードでそのサブツリーが BCD であり、同様に B がルート ノードでそのサブツリーが EF です。等々...
ツリーの基本概念
- ノードの次数: ノードに含まれるサブツリーの数はノードの次数と呼ばれます; 上の図に示すように、A は 6、B は 0、D は 1、F は 3 です。
- リーフ ノード (ターミナル ノード):次数 0 のノードはリーフ ノードと呼ばれます; 上の図に示すように: B、C、H、I などのノードはその下にサブツリーがなく、次数は 0 です。 。
- ブランチ ノード (非終端ノード): 次数が 0 ではないノードはブランチ ノードです; 上の図に示すように: D、E、F、G... および他のノードはすべて部分木を持ち、次数 !=0、これはブランチノードです。
- 親ノード (親ノード):上の図に示すように、ノードに子ノードが含まれる場合、このノードはその子ノードの親ノードと呼ばれます: A には B が含まれており、A は B の親ノードです。
- 子ノード(子ノード): ノードに含まれるサブツリーのルート ノードをノードの子ノードと呼びます。上に示すように、B は A に含まれ、B は A の子ノードです。
- 兄弟ノード:同じ親ノードを持つノードは兄弟ノードと呼ばれます。上の図に示すように、B、C、D、F、および G には兄弟ノードである共通の親ノード A があり、同じことが当てはまります。 PとQ。
- ツリーの次数: ツリーでは、最大のノードの次数がツリーの次数と呼ばれます。上の図に示すように、すべてのノードのうち、A ノードの次数は 6 であるため、ツリーの次数はは6です。
- ノードのレベル: rootの定義から始まり、ルートが第 1 レベル、ルートの子ノードが第 2 レベル、A ノードには 4 つのレベルがあります。
- ツリーの高さまたは深さ:ツリー内のノードの最大レベル; 上に示すように: ツリーの高さは 4 です。
- いとこノード: 親が同じレイヤー上にあるノードは、お互いのいとこノードです。上の図に示すように、H、I、N、および M は互いのいとこノードです。
- **ノードの祖先: **ルートからこのノードのブランチ上のすべてのノードまで; 上の図に示すように: A はすべてのノードの祖先です。
- 子孫:ノードをルートとするサブツリー内のノードはすべて、ノードの子孫と呼ばれます。上に示すように、すべてのノードは A の子孫です。
- フォレスト: m (m>0) 個の互いに素なツリーの集合をフォレストと呼びます。
上記の概念のうち、理解すべき重要な点は、リーフ ノード、親ノード、子ノード、ツリーの高さと深さ、およびノードの子孫です。
ツリー構造の実装
上記を読んだ後、コードを使用して上記の構造を実装する方法を考えたことはありますか? 次に、ツリー構造をより効率的に実装する方法を考えてみましょう。
ツリーを実現する方法は数多くあり、データ表現は非常にシンプルですが、ノードの下にある子の数はツリー内で明確に指定されていないため、ツリーの最優先事項は各ノードの子をどのように格納するかである必要があります。 。
**方法 1:** ツリーの次数が N であると仮定します。
struct TreeNode
{
int data; // 用来存储数据的成员
struct TreeNode* subs[N]; // 用来存储孩子结点(N个度)的指针数组
}
//这种方式的缺点显而易见,很有可能会出现不少的数组空间未使用导致内存空间的浪费
**方法 2:** 木の程度は記載されません。
struct TreeNode
{
int data; // 用来存储数据的成员
SeqList s; // 在顺序表里存结点的指针
}
typedef struct TreeNode SLDataType; // 这里是一个顺序表SeqList
// 这种方式的缺点就是,结构过于复杂。因为SeqList这里已经是一个二级指针
**方法 3:** 構造配列ストレージ (親表現)
// 这种方法的主要操作就是,通过下面的结构体TreeNode存储有效信息,最后将其存储到parentArr数组中。
struct TreeNode // 数组存储信息
{
int data; // 结点数据
int parent; // 父亲结点的下标位置
}
struct TreeNode parentArr[10]; // 存储结点的数组
// 在这个TreeNode结构体中,data表示当前结点的数据,而parent为这个结点的父节点的下标位置。根节点标记为-1;写入数据后存入结构体数组;
// 具体存储结果可以参考如下举例
データ | あ | B | C | D | E | H | 私 | J |
---|---|---|---|---|---|---|---|---|
親 | -1 | 0 | 0 | 0 | 0 | 3 | 4 | 4 |
添字 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
データ: ノードデータ
parent: 親ノードの添字位置
添字: 配列の添字
上記の 3 つの方法にはそれぞれ長所と短所がありますが、最も一般的で実用的な方法は ------左子右兄弟
法 4 : 左子右兄弟法です。よく使われるツリー構造も左子右兄弟構造です。
まずルート ノードから開始し、子ノードの最初の子から子ノードまでを左から右に記録し、次にbrother を使用して兄弟ノード C を見つけ、次に child を使用して子ノードを記録して再帰を継続します。このようにして、親ノードに子ノードがいくつあっても、記録することができます。たとえば、ノード A の子ノードを見つけたい場合は、最初に child から child までの最初の子に移動し、次にリンク リストをたどるのと同じように他の子ノードを見つけます。
同時に、この構造の欠点を見てみますと、この構造には無駄な空間がなく、横断する際に直結の関係になっていることがわかります。今のところ、この構造にはデメリットはありません!
参照コード:
typedef int DataType;
struct Node
{
DataType data; // 节点数据
struct Node* firstChild; // 第一个孩子的节点(永远指向第一个孩子,叶子则为NULL)
struct Node* pNextBrother; // 指向其下一个兄弟节点(永远指向其的兄弟节点,否则为NULL)
};
データ | あ | B | C | D | E | F | G | H | 私 |
---|---|---|---|---|---|---|---|---|---|
第一子 | B | D | G | ヌル | H | ヌル | ヌル | ヌル | ヌル |
p次兄弟 | ヌル | C | ヌル | E | F | ヌル | ヌル | 私 | ヌル |
二分木の概念と構造
二分木の概念:
バイナリ ツリーは、次のようなノードの有限セットです。
空の場合もあります。
これは、ルート ノードと 2 つのバイナリ ツリー (左サブツリーおよび右サブツリーとも呼ばれます) で構成されます。
上の図からわかるように:
バイナリ ツリーには 2 より大きい次数を持つノードはありません (最大次数は 2)
二分木は部分木が左右に分かれており、順序を逆にすることができないため、二分木は順序付き木となります。
どのバイナリ ツリーでも次のような状況が発生する可能性があります。
特別な二分木
完全なバイナリツリー
各層のノード数が最大値に達すると、バイナリ ツリーは完全なバイナリ ツリーになります。
完全なバイナリ ツリーのプロパティ
すべてのリーフ ノードは最後の層にあります。
すべてのブランチ ノードには 2 つの子があります。
完全なバイナリ ツリーの層数が k の場合、ツリーの k 番目の層には $2^{k-1}$ ノードがあります。
完全な二分木の層数が h の場合、ツリー内のノードの総数は2 h − 1 2^{h-1}となります。2h − 1;
完全なバイナリ ツリーに N 個のノードがある場合、ツリーの高さは $log_2 (N+1) $ になります。
完全な二分木
完全なバイナリ ツリーは非常に効率的なデータ構造であり、完全なバイナリ ツリーは完全なバイナリ ツリーから派生します。深さが K でノードが n のバイナリ ツリーの場合、各ノードが、深さのある完全なバイナリ ツリー内の 1 から n までの番号が付けられたノードと 1 対 1 対応する場合に限り、完全なバイナリ ツリーと呼ばれます。Kさんの 完全なバイナリ ツリーは特別な種類の完全なバイナリ ツリーであることに注意してください。
完全なバイナリ ツリーのプロパティ:
-
最初の N-1 フロアは満席です。
-
最後のレイヤーは完全ではありませんが、最後のレイヤーは左から右に連続しています。(たとえば、上の図の最後の層で、ノードに右の子のみがあり、左の子がない場合、それは完全な二分木ではありません)
-
高さ h の完全な二分木の場合、ノードの範囲は [ 2 h − 1 2^{h-1}2h − 1、2 h − 1 2^h - 12h−1 ]。
二分木の性質
バイナリ ツリーのプロパティには次の点があります。
-
ルート ノードの層数が 1 と指定された場合、空ではない二分木のi 番目の層には最大2 i − 1 2^{i-1}が存在します。2i − 1ノード。
-
ルート ノードの層数を 1 に指定した場合、深さ h のバイナリ ツリー内のノードの最大数は2 h − 1 2^h - 1となります。2h−1 .
(各層の番号は等比数列です)
-
任意の二分木について、次数が 0 の場合、葉ノードの数はn 0 n_0です。n0、次数 2 の分岐ノードの数はn 2 n_2です。n2、すると、n 0 n_0になります。n0=n 2 n_2n2+1 (0 の次数は常に 2 の次数より 1 大きい)
-
ルートノードのレイヤー番号が 1 に指定されている場合、** nnになります。nノードhhh,**h = log 2 ( n + 1 ) h = log_2(n+1)h=ログ_ _2( n+1 )
-
n 個のノードを持つ完全な、配列の上から下、左から右の順序に従ってすべてのノードに 0 から番号が付けられている場合、シーケンス番号 i のノードについては次のようになります。
-
i>0 の場合、i 位置ノードの親番号: (i-1)/2 ; i=0、i はルート ノード番号であり、親ノードはありません。
-
2i+1<n の場合、左の子のシリアル番号: 2i+1, 2i+1>=n それ以外の場合、左の子はありません。
-
2i+2<n の場合、右側の子のシリアル番号: 2i+2, 2i+2>=n、そうでない場合は右側の子はありません。
-
-
完全なバイナリ ツリーは完全なバイナリ ツリーである必要がありますが、完全なバイナリ ツリーは必ずしも完全なバイナリ ツリーである必要はありません。
二分木記憶構造
バイナリ ツリーは通常、シーケンシャル構造とチェーン構造の 2 つの構造を使用して格納できます。
シーケンシャル構造
シーケンシャル構造ストレージはストレージに配列を使用します**バイナリ ツリー シーケンシャル ストレージは物理的には配列であり、論理的にはバイナリ ツリーです。**ただし、実際には配列に格納されるのはヒープのみです。ヒープについては次の章で具体的に説明します。
バイナリ ツリーの順次ストレージは、一般に、完全なバイナリ ツリーを表す場合にのみ適しています。これは、完全なバイナリ ツリーが存在しないとスペースが無駄になるためです。
上の図からわかるように、順次保存すると、非常に規則的に保存され、各ノードがよく表現されていることがわかります。順次ストレージの方法では、親ノードまたは左右の子の添え字の位置は、ノードの添え字を通じて計算できます。
親が配列内の親ノードの添字であると仮定すると、次のようになります。
leftchild = 親 * 2 + 1 rightchild = 親 * 2 + 2
child を子ノードとすると、左右に関係なく次のようになります。
親 = (子 - 1) / 2
//これは、後のヒープの実装において非常に重要な役割を果たします。
チェーンストレージ
二分木の連結記憶構造とは、二分木を表現するために連結リストが使用されること、すなわち、要素の論理的関係を示すためにリンクが使用されることを意味する。通常の方法では、リンク リストの各ノードは、データ フィールドと左右のポインタ フィールドの 3 つのフィールドで構成され、左右のポインタは、左の子と左の子がポイントするリンクのストレージ アドレスを与えるために使用されます。ノードの右の子が見つかります。
チェーン構造は2フォークチェーンと3フォークチェーンに分かれます。
バイナリ チェーン: 左右の子を指す 2 つのポインタがあります。
トライデント チェーン: 左右の子を格納する 2 つのポインターだけでなく、親ノードの位置を格納するポインターもあります。
一般にバイナリ チェーンが使用され、赤黒ツリーなどの高レベルのデータ構造ではトリプル チェーンが使用されます。
考える質問
二分木には 399 個のノードがあり、その中に次数 2 のノードが 199 個ある場合、二分木内の葉ノードの数は ( ) になります。
A. そのような二分木は存在しません
B、200
C、198
D、199
2n 個のノードを持つ完全な二分木では、葉ノードの数は ( ) です。
あ、ん
B、n+1
C、n-1
D、n/2
完全な二分木のノード数は 531 で、この木の高さは ( )
あ、11
B、10
C、8
D、12
767 個のノードを持つ完全なバイナリ ツリー、リーフ ノードの数は ()
あ、383
B、384
C、385
D、386
参考回答:
答え: B、
分析: リーフ ノード:次数 0 のノードはリーフ ノードと呼ばれます。バイナリ ツリーの性質上、次数 2 のノードよりも次数 0 のノードが常に 1 つ多く存在します。質問では次数 2、次に他の次数です。 199+1=200 個の葉ノード (次数 0) があります。
答え:A、
分析: この質問では、プロパティ導出方法が使用されます。
完全な二分木の次数 0 のノードの数をx 0 x_0とします。バツ0、次数 1 のノードの数はx 1 x_1です。バツ1、次数 0 のノードの数はx 2 x_2です。バツ2、タイトルによれば、2n 個のノードがあります
∴ 利用可能なノードの総数の方程式は次のとおりです: x 0 x_0バツ0+ x 1 x_1バツ1+ x 2 x_2バツ2= 2n ;
∵ どの二分木でも、次数 0 の木は次数 2 の木よりも常に 1 大きい、つまりx 2 x_2バツ2= x 0 x_0バツ0-1;
∴方程式2 x 0 x_0を取得します。バツ0+ x 1 − 1 x_1 - 1バツ1−1 = 2n;
質問でリーフ ノードの数を尋ねます。つまり、x 0 x_0を尋ねます。バツ0数字はいくつですが、x 1 x_1になります。バツ1(次数 1) を取得できない場合は、完全な二分木の性質を確認してみましょう。
完全な二分木では、最初の N-1 レベルはいっぱいですが、最後のレベルはそうではありませんが、最後のレベルは左から右に連続しています。これで、完全な二分木における次数 1 のノード数の範囲はx 1 x_1であると結論付けることができます。バツ1∈ [0, 1]、次数 1 のノードは最大 1 つです。
もう一度この質問に戻りますが、方程式2 x 0 x_0バツ0+ x 1 x_1バツ1-1 = 2n ;、x1 の値の範囲が [0, 1] の場合、x 1 x_1バツ1
0 または 1 を指定できます。そのx 1 x_1バツ1何が良いでしょうか?
x 1 x_1のときバツ1= 0、ノード方程式は 2 x 0 x_0です。バツ0- 1 = 2n、x 0 x_0バツ0= (2n+1) ÷ 2;
x 1 x_1のときバツ1= 1、ノード方程式は 2 x 0 x_0です。バツ0= 2n, x 0 x_0バツ0= n;
∵ ×1×_1バツ1= 1、x 0 x_0バツ0整数以外の数値は破棄されます。
∴ ×1×_1バツ1= 1 は質問の意味と一致しているため、答えは A n です。
この問題は、完全な二分木のプロパティを組み合わせる必要があるだけでなく、問題を解くための公式と組み合わせる必要もあります。非常に良い問題です。同じ種類の 4 番目の問題を計算してみてください。
答え: B、
分析: 完全な二分木の性質上、高さ h の完全な二分木には [ 2 h − 1 2^{h-1} のノード範囲があると述べました。2h − 1、2 h − 1 2^h - 12h−1 ]。それでは、その推論プロセスはどのようなものであるべきでしょうか?
完全な二分木では、最大の状況は、完全な二分木の最後の層のノードの数がいっぱいになることです。これは完全な二分木なので、最大値は2 h − 1 2^h -となります。12h−1の場合、最小値はいくらでしょうか? 想像してみてください。最後の層が最も小さい状況では、最後の層にはノードが 1 つだけあり、そのすべてが前の層のノードの数に 1 を加えたものになります。したがって、層の最大数が h のとき、次の数は次のようになります。 h-1 層のノードの数は2 h − 1 − 1 2^{h-1}-12h − 1−1に最後の層のノードを加えたものは2 h − 1 − 1 + 1 2^{h-1}-1+12h − 1−1+1であるため、ノード番号範囲が [2 h − 1 2^{h-1}2h − 1、2 h − 1 2^h - 12h−1 ]。
まとめると、 h = 11 の場合、完全な二分木のノード数の範囲は [1024, 2047] になります。 h = 10 の場合、完全な二分木のノード数の範囲は [512, 1023] になります。 h = 8 の場合、完全な二分木のノード数の範囲は [128, 255] h = 12 の場合、完全な二分木のノード数の範囲は [2048, 4095]
したがって、答えは B 10 です。
答え: A 383、
分析: 質問 2 と同じ。
裏に書かれた言葉
これで二分木の基本は完成です!バイナリ ツリーを学習する場合、通常、バイナリ ツリーの追加、削除、確認、変更を直接学習することはありません。バイナリ ツリーは通常、データの保存に直接使用されませんが、さまざまなニーズを満たすためにバイナリ ツリーによってさまざまな変換が実行されるためです。学習では二分木の場合は二分木の構造と概念を学ぶことに重点を置き、その後、AVLや赤黒木など、実際にデータを保存できるようになると、追加、削除、確認、修正を学びますバイナリ ツリーの完全な構造操作を開始するときは、コンテンツ ------ヒープを学習する必要があります。これは、バイナリ ツリーの学習を開始する前に理解する必要があるデータ構造です。
エピローグ
作成するのは簡単ではありません。この記事が役に立ったと思われる場合は、 「いいね!」と視聴 + フォローを忘れないでください。
今後は、WeChat 公開アカウント「01 プログラミング小屋」に最新記事をいち早くお届けします。小屋をたどって迷わずプログラミングを学べます。見逃さないように公開アカウントのフォローもお忘れなくそれ!