注: 記事内の色付きのコードはすべて Visual Studio 2022 コンパイラで書かれています。この記事は C 言語のデータ構造を手書きしたものです。記事にはいくつかの変更があり、オリジナルのものではありません。
目次
学習目標
- ツリーの定義、表現方法、関連用語、基本概念を理解します。
- バイナリ ツリーの再帰的定義とバイナリ ツリーのプロパティに習熟し、対応する証明方法を理解します。
- バイナリ ツリーの2 つの保存方法、特性、および適用範囲に習熟してください。
- バイナリ ツリーのさまざまな順序のトラバーサル アルゴリズムに習熟し、3 つのトラバーサル アルゴリズムを柔軟に使用してバイナリ ツリーに他の操作を実装し、単純なアプリケーションの問題を解決できるようになります。
- 二分木の手がかりと本質を理解し、順序手がかりツリー内の特定のノードの順序どおりの先行ノードと順序どおりの後続ノードを見つける方法を習得します。
- ツリー、フォレスト、バイナリ ツリー間の変換方法、およびツリーとフォレストの走査方法を習得します。
- 最適な二分木の特徴を理解し、ハフマン木の構築方法とハフマン符号化方法を習得します。
5.1 .ツリーの基本概念と用語
ツリー構造は非線形データ構造の重要なタイプであり、ツリー内のノード間には明確な階層関係があり、ノード間には分岐があり、実際のツリーと非常によく似て 行政組織や人間社会の系図など、客観世界には木構造が多数存在し、それを木構造によって鮮やかに表現することができる。コンピュータアプリケーションの分野でもツリー構造は広く使われており、例えば、コンパイラではソースプログラムの文法構造を表現するためにツリー構造が使用され、データベースシステムでは情報を整理するためにツリー構造が使用され、コンピュータグラフィックスではツリー構造が使用されます。画像の関係性などを表すツリー構造。
実際には、ツリー構造で説明できる現実的な問題が数多くあります。例えば、大学の運営組織は、大学が各学部やカレッジ(学科)を統括し、学部が各学科を統括し、学部が各教育研究部門を統括するツリー構造になっています。この管理組織の関係は、図 5.1 に示す樹形図で表すことができ、大学を「根」、学部や学部が「枝」、学部が「枝」となり、逆さまの木のようになります。 、オフィス、教育研究オフィスは「Leaves」です。大学を根とするツリーは大きなツリーであり、これは枝や大学を根とする小さなツリー(サブツリー)に分割することができ、それぞれのサブツリーはさらにいくつかのサブツリーに分割することができます。ツリー構造が再帰的な構造であることがわかります。
1. ツリーの定義
ツリーは、n (n≥0) 個のノードの有限集合Tです。これは、空のセット(空のツリーは n=0 を意味します) または空でないセットのいずれかです。空ではないツリーの場合:
1. Rootと呼ばれる特定のノードが1 つ だけ存在します。
2. n>1 の場合、残りのノードはm (m>0)個の互いに素な有限集合T 1 、T 2 、... 、 T m に分割でき、それぞれはそれ自体がツリー Tree と呼ばれます。ルートのサブツリー。たとえば、図 5.2 (a) は 11 個のノードを持つツリーを示しています。A はルート ノードであり、残りのノードは 3 つの互いに素なサブセットに分割されています: T1={B, E, F, J , K}, T2= {C, G}, T3={D, H, I); T1、T2、および T3 はすべてルート A のサブツリーであり、それ自体がサブツリーです。
2. ツリー表現
ツリーを表現する方法はたくさんありますが、最も一般的に使用されるのはツリー図表現です。樹形図表現では、図 5.2 (a) に示すように、通常、ノードは円で表され、ノード名は通常、円の内側または隣に書かれます。この表現は非常に直感的であるため、本書ではほとんどの場合、ツリー構造を表すために使用されます。
アプリケーションが異なれば、ツリーの表現方法も異なります。通常、ツリー表現以外に 3 つの表現方法があり、図 5.2 (a) のツリーは図 (b)、(c)、(d) のように表現できます。入れ子の集合の形式で表現; 図 5.2 (c) は凹面表現を使用; 図 5.2 (d) は一般化されたテーブルの形式で表現され、ツリーのルートはテーブル内に、亜木林、左側。
3.基本用語
ツリー構造の関連知識をよりよく理解し、習得するには、まずツリー構造の基本用語をよく理解しておく必要があります。
ツリーノードには、データ要素とそのサブツリーを指すいくつかのブランチが含まれています。ノードが持つサブツリーの数をノードの次数と呼びます。たとえば、図 5.2 (a) のルート ノード A には 3 つのサブツリーがあるため、その次数は 3、C の次数は 1、E の次数は 0 になります。木の次数の最大値を木の次数といい、図 5.2 (a) の木の次数は 3 です。次数0 のノードはリーフノードまたはターミナル ノードと呼ばれ、図 5.2 ( a) のノード E 、 J、K、G、H、Iはすべてツリーのリーフ ノードです。非ゼロ次数非終端ノードまたは分岐ノードと呼ばれます。ルート ノードに加えて、ブランチ ノードも内部ノードと呼ばれ、ルート ノードは開始ノードとも呼ばれます。
ツリー内のノードのサブツリーのルートはそのノードの子と呼ばれ、したがって、そのノードは親または子ノードの親ノードと呼ばれます。たとえば、図 5.2 (a) に示すツリーでは、B がサブツリー T1 のルート、B が A の子、A が B の親になります。同じ親の子は互いに兄弟です。別の例は、ノード BCD が互いに兄弟であることです。上記の親子関係をさらに一般化すると、ノード B はノード K の祖父であると考えることができます。
k iがk i +1 (1≤ i≤j)の親ノードであるようなノード シーケンスk 1 、K 2 、... 、K j が ツリー内にある場合、ノード シーケンスはaと呼ばれます。 k 1からk jへのパス。たとえば、図 5.2(a) には、ノードAからKまでのパスABFKがあり、そのパス長は 3 です。明らかに、ツリーのルートからツリー内の他のノードへのパスは 1 つだけです。
ツリー内にノード k iからk jへのパスがある場合、ノード k i はk jの祖先であり、ノード k jはk iの子孫であると言われます。たとえば、図 5.2 (a) に示すツリーでは、ノード J の祖先は A、B、F であり、ノード B の子孫は E、F、J、K です。
ツリー内のノードのレベルは、root から計算されます。ルートは最初のレベルです。他のノードのレベルは、その親ノードのレベルに 1 を加えたものに等しくなります。ツリー内のノードの最大レベルは、ツリーの深さ( Depth ) または高さ( Height ) 。図 5.2 (a) に示すように、ノード F のレベルは 3、ツリーの高さは 4 です。
ツリー内のノードのサブツリーが左から右に順序付けされているように見え、交換できない 場合、そのツリーは順序付きツリーと呼ばれ、それ以外の場合は順序なしツリーと呼ばれます。
フォレストは、m (m ≥ 0) 個の互いに素なツリーの集合です。ツリーのルート ノードを削除すると、ツリーのサブツリーで構成されるフォレストが得られます。フォレスト内のすべてのツリーをサブツリーとして取得し、それらすべてをルート ノードで接続すると、フォレストはツリーになります。
5.2 .二分木
バイナリ ツリーは重要なタイプのツリー構造であり、実際のアプリケーションでは非常に重要です。多くの現実的な問題から抽象化されたデータ構造は二分木の形式であることが多く、一般的な木でも二分木の形式に変換することは容易であり、二分木の記憶構造とアルゴリズムは比較的単純です。バイナリ ツリーの保存、操作、およびアプリケーションについて詳しく説明します。
5.2.1 .二分木の定義と性質
1. 二分木の定義
バイナリ ツリーはn (n≥0) 個のノードの有限集合であり、各ノードには最大2 つのサブツリーがあります。これは空のセットであるか、ルート ノードと、ルートの左サブツリーと右サブツリーと呼ばれる 2 つの互いに素なバイナリツリーで構成されます。
上記の二分木の定義から、二分木の定義は再帰的であることがわかり、この定義によれば、二分木の図 5.3 に示すような種の基本形を導き出すことができます。このうち、図 5.3 (a) は空のバイナリ ツリー、図 5.3 (b) はルート ノードが 1 つだけあるバイナリ ツリー、図 5.3 (c) はルート ノードと左側のサブツリーのみを持つバイナリ ツリー、図 5.3 (c) はルート ノードと左側のサブツリーのみを持つバイナリ ツリーです。 5.3 (d) はルートのみを持つ 2 分木 ノードと右側のサブツリーを持つ 2 分木 図 5.3 (e) はルートと左右のサブツリーを持つ 2 分木です。
2.二分木の性質
二分木には、以下で説明する非常に重要な特性がいくつかあります。
特性1:二分木のi 番目のレベルには最大2 i -1 個のノードが存在します(i≥1)
数学的帰納法を使用して次のことを証明します。
- i=1 の場合、ルート ノードは 1 つだけ、つまり 2i-1=20=1 となり、命題は真になります。
- すべての j (1≤j≤i) について命題が成り立つと仮定します。つまり、j 番目の層には最大 2i-1 個のノードが存在します。
- 帰納的仮定によれば、i-1 番目の層には最大 2i-2 個のノードが存在します。二分木の各ノードの次数は最大 2 であるため、i 番目のレベルのノードの数は、i-1 レベルの最大ノード数の 2 倍、つまり 2×2i-2= になります。 2i-1 ノードの点なので、命題が成り立ちます
特性2:深さkの二分木には最大 2 k -1 個のノードがあります ( k ≥1)
証明: 深さ k のバイナリ ツリーに含めることができるノードの最大数は、バイナリ ツリーの各レベルのノードの最大数の合計である必要があります。特性 1 から、深さ k の二分木の最大ノード数は次のとおりであることがわかります。
したがって、命題は成立する。
特性3: 任意の二分木Tについて、終端ノードの数がn 0で、次数 2 のノードの数がn 2である場合、n o = n 2 +1
証明: n1 を二分木T内の次数 1 のノードの数とする。バイナリ ツリー内のすべてのノードの次数は 2 を超えないため、バイナリ ツリー T 上のノードの総数は次のようになります。
n=n0+n1+n2 (5-1)
一方、次数 1 のノードには 1 つの子があり、次数 2 のノードには 2 つの子があることを意味するため、ツリー内の子ノードの総数は n1+2n2 になります。ツリーでは、ルート ノードのみがどのノードの子ノードでもないため、バイナリ ツリー内のノードの総数は次のように表すことができます。
n=n1+2n2+1 (5-2)
式(5-1)と式(5-2)より、n0=n2+1となり、命題が成立します。
完全なバイナリ ツリーと完全なバイナリ ツリーは、バイナリ ツリーの 2 つの特殊なケースです。
- 完全なバイナリ ツリー:深さがkで 2 k - 1 個のノードを持つバイナリ ツリーは、完全なバイナリ ツリーと呼ばれます。図 5.4 (a) に示す二分木は、深さ 4 の完全な二分木です。この木の特徴は、各レベルのノード数が最大に達しているため、次数 1 のノードが存在せず、すべてのレベルのノードが存在することです。リーフ ノードはすべてk 番目の層にあります。
- 完全な二分木:二分木の深さがkの場合、その最初のk -1 レベルは完全な二分木であり、最下位レベル (つまり k 番目のレベル) のノードはレベルの左端の位置に集中しています。の場合、この二分木は完全二分木と呼ばれます。図 5.4 (b) に示す二分木は完全な二分木ですが、図 5.4 (c) に示す二分木は最下層のノード L が左端にないため完全な二分木ではありません。
明らかに、完全なバイナリ ツリーは完全なバイナリ ツリーである必要がありますが、完全なバイナリ ツリーは必ずしも完全なバイナリ ツリーである必要はありません。
プロパティ4 nノードを持つ完全な二分木の深さは、 ⌊ logn ⌋ +1 または ( ⌈ log(n+1) ⌉ )です。
証明: バイナリ ツリーの深さがkであると仮定すると、完全なバイナリ ツリーの定義によれば、その最初の k-1 レベルは、深さが k-1 で、合計 2k-1- の完全なバイナリ ツリーになります。 1ノード。二分木の深さはkなので、k番目のレベルにはノードがあり、二分木のノード数はn>2k-1-1となる。特性 2 から、n≤2k-1 であることがわかり、次のようになります。
2k-1-1<n≤2k-1
このことから、2k-1<n≤2k であることが推測できます。3 つの項の対数を取ると、k-1≤logn<kになります。k は整数なので、⌊logn⌋+1 となります。
5.2.2 .バイナリツリーの記憶構造
1. シーケンシャルストレージ構造
n個のノードを持つ完全なバイナリ ツリーを順番に保存する 場合、ツリー内の各ノードにツリーのルートから始めて上から下に番号が付けられ、各レベルで左から右に番号が付けられている限り (番号が 0 から始まると仮定して) , すると、図 5.5 (a) に示すように、二分木構造全体を反映した線形シーケンスが得られます。次に、図 5.5 (b) に示すように、各ノードの番号を添字として使用して、各ノードの値を 1 次元配列 bt に格納する必要があります。
完全な二分木の定義からわかるように、完全な二分木では、最下層を除いて、各層のノードは最大値に達し、各層のノード数はノード数のちょうど 2 倍になります。前のレイヤーで。したがって、ノードの番号から、その親と左右の子の番号を推定できます。
たとえば、ノード qi の番号が i (0≤i<n) であるとすると、次のようになります。
① i= 0 の場合、q iはルート ノードで親がありません; それ以外の場合、 q iの親ノード番号は⌊ (i-1)/2 ⌋です。
② 2i+1 < n の場合、 q i の左の子ノード番号は2i+1 であり、そうでない場合、q i は左の子を持たない、つまり、q i は葉ノードでなければなりません。
③ 2i+2<n の場合、qi の右の子ノード番号は 2i+2 であり、そうでない場合、qiには右の子ノードがありません。
完全なバイナリ ツリーの場合、シーケンシャル ストレージ構造を使用するのは明らかに簡単で、ストレージ スペースも節約できます。しかし、一般的なバイナリ ツリーでは、シーケンシャル ストレージを使用する場合、配列内のノードの相対位置を使用してノード間の論理関係を表すために、完全なバイナリ ツリーにするためにいくつかの仮想ノードを追加する必要があります。この方法でバイナリ ツリーにノードを格納すると、ストレージ スペースの無駄が発生し、最悪の場合、深さがkでノードがkだけの単一のバイナリ ツリーには 2k-1 ノードのストレージ スペースが必要になります。たとえば、ノードが 4 つしかないバイナリ ツリーの場合、実際には存在しないいくつかの仮想ノード " @ " を追加すると、図 5.6 (a) に示すように完全なバイナリ ツリーになり、対応するシーケンシャル ストレージ構造は次のようになります。図 5.6 (b) を示します。
2.チェーンストレージ構造
上で紹介したバイナリツリーの逐次記憶構造から判断すると、これは完全なバイナリツリーにのみ適用できますが、一般的なバイナリツリーの場合、記憶領域を無駄にするだけでなく、バイナリ内のノードを挿入または削除するときに大量の移動が必要になります。ツリーのノード。したがって、一般に、バイナリ ツリーの保存にはチェーン ストレージがよく使用されます。異なる (ノード) 構造を設計すると、異なる形式のチェーン ストレージ構造を形成できます。Eryou ツリーのリンク ストレージ表現では、各ノードに値フィールド、左ポインタ フィールド、右ポインタ フィールドの 3 つのフィールドが設定され、データは値フィールドを表すのに使用され、Iが一般的に採用されます。図 5.7 (a) に示すように、 childおよびrightChildがそれぞれ表され、左右のサブツリー (子) を指すポインタ フィールド。
対応するタイプの説明は次のとおりです。
バイナリチェーン:
構造体 BinaryNode { 文字 データ。 struct BinaryNode * left; struct BinaryNode * right; }binaryNode、binaryTree; |
三叉チェーン:
構造体 TriadicNode { 文字 データ。 struct TriadicNode * left; struct TriadicNode * right; struct TriadicNode * 親; triadicNode、triadicTree; |
バイナリ ツリーには、そのルート ノード (つまり、開始ノード) と、バイナリノードタイプのすべてのノードを指すバイナリツリー タイプのヘッド ポインタ があり、バイナリ ツリーのチェーン ストレージ構造を構成し、バイナリ ツリータイプと呼ばれますバイナリリンクリスト。たとえば、図 5.8 (a) に示した二分木のバイナリ連結リストは、図 5.8 (b) に示されます。
バイナリ リンク リストは、一般的に使用されるバイナリ ツリー ストレージ構造です。バイナリ ツリーに対する関連操作では、通常、このリンク ストレージ構造が使用されます。ただし、ノードの親の検索を容易にするために、ノード構造へのポインタを追加することもできます。ノード構造を 親図 5.7(b)に示します。
5.3 .二分木の演算
5.3.1 .二分木の生成
いわゆる生成とは、実際にはバイナリ ツリーの記憶構造を確立することを意味します。
バイナリ ツリーの逐次記憶構造を確立するのは比較的簡単であるため、ここでは説明しませんが、以下では、バイナリ ツリーの連結記憶構造、つまりバイナリ リンク リストを確立する方法に焦点を当てます。入力バイナリ ツリー ノードの形式に応じて、バイナリ リンク リストを構築する方法も異なります。バイナリリンクリストを作成する 2 つの方法を次に示します。
1.一般化リスト--リンクリストアルゴリズム
このアルゴリズムは比較的単純ですが、一般化されたテーブルを使用してバイナリ ツリーを表現することについての深い理解が必要です。左右のサブツリーがあるため、ツリーを表す通常の一般化されたテーブルとは異なります。たとえば、図 5.9 に示すバイナリ ツリーの一般化されたテーブル表現は次のとおりです。
(A(B(,D(E,F)),C))
左括弧の近くのノードは左側のサブツリー上にあり、コンマの右側のノードは右側のサブツリー上にあります。
バイナリリンクリスト を構築するアルゴリズムは、ノードの親ポインタをスタック(3.1.2節のシーケンシャルスタックカプセル化)を用いて格納し、文字に応じて4つの異なる状況で処理される。一般化されたテーブルを読み込みます。
#define _CRT_SECURE_NO_WARNINGS //C4996 警告を回避する #include <stdio.h> #include <stdlib.h> #include <文字列.h> #include <math.h> #include <Windows.h> #include "serialStack.h" #include "dynamicArray.h" #define プリント 1 #define EMPTY_NODE '*' 構造体 BinaryNode { 文字* データ; struct BinaryNode * left; struct BinaryNode * right; }; 構造 体バイナリツリー { struct BinaryNode * ルート; 整数の 深さ; }; 構造体 TreeArray { 内部 座席; struct BinaryNode * ノード; }; 構造 体ツリーリスト { 整数 サイズ; struct TreeArray * ツリー配列; }; void linkChangeArray(struct BinaryNode* node, struct TreeArray* treeArray, int index) { if (node == NULL) { return; } treeArray[index].node = node; linkChangeArray(node->left, treeArray, index * 2 + 1); linkChangeArray(node->right, treeArray, index * 2 + 2); } int leftGetParent(int index) { return (index - 1) >> 1; } int rightGetParent(int index) { return (index - 2) >> 1; } int getLeftTree(int index) { return (index << 1) + 1; } int getRightTree(int index) { return (index << 1) + 2; } void markLeftIndex(struct TreeList* treeList, int index, int counter); void markRightIndex(struct TreeList* treeList, int index, int counter); void markParentIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0) { if (treeList->treeArray[index].seat == -1) { treeList->treeArray[index].seat = counter; markRightIndex(treeList, getRightTree(index), ++counter); } else { markParentIndex(treeList, leftGetParent(index), counter); } } } void markLeftIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } } void markRightIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, rightGetParent(index), ++counter); } } void markTreeIndex(struct TreeList* treeList, int index) { int counter = 1; treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } void printBinaryTree(struct TreeList* treeList) { int seat, size = treeList->size, front = 0, jump = 1, add = 2; for (int i = 0; i < size; i++) { seat = treeList->treeArray[i].seat - front; for (int j = 0; j < seat; j++) { printf(" "); front++; } if (treeList->treeArray[i].node == NULL) { printf("%c", EMPTY_NODE); } else { printf("%s", treeList->treeArray[i].node->data); } CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\\"); coord.X = ipBuffer.dwCursorPosition.X - 2; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("/"); coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); front++; if ((i + 1) % jump == 0) { CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\n"); jump = jump + add; add = add * 2; front = 0; } } } void drawBinaryTree(struct BinaryTree* binaryTree) { double size = floor(pow(2, binaryTree->depth)) - 1; struct TreeArray* treeArray = malloc(sizeof(struct TreeArray) * size); struct TreeList* treeList = malloc(sizeof(struct TreeList)); if (treeArray == NULL || treeList == NULL) { #if PRINT printf("绘制二叉树时。内存空间不足!\n"); #endif return; } for (int i = 0; i < size; i++) { treeArray[i].seat = -1; treeArray[i].node = NULL; } treeList->size = size; treeList->treeArray = treeArray; linkChangeArray(binaryTree->root, treeArray, 0); markTreeIndex(treeList, (int)size >> 1); printBinaryTree(treeList); } void stockpileBinaryNodeData(struct BinaryNode* currentNode, struct ArrayList* arrayList, char pointer) { if (currentNode != NULL && currentNode->data == NULL && arrayList != NULL) { currentNode->data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList == NULL; currentNode == NULL; } } int setBinaryNodeData(struct BinaryNode* currentNode, struct ArrayList* arrayList, char pointer) { if (currentNode != NULL && currentNode->data == NULL && arrayList != NULL) { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) { #if PRINT printf("创建二叉树结点赋值失败,内存空间不足!\n"); #endif return; } arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; return 1; } return 0; } struct BinaryTree* createListBinaryTree(char* list) { int index = 0, left = 1, probe = 0, depth = probe; char pointer = list[index]; struct SequenceStack* stack = initSequenceStack(); struct BinaryNode* currentNode = NULL; struct BinaryNode* stackTopNode = NULL; struct BinaryNode* treeRoot = NULL; struct ArrayList* arrayList = NULL; while (pointer != '\0') { if (pointer == ' ') { index++; pointer = list[index]; continue; } switch (pointer) { case '(': left = 1; probe++; stack->push(currentNode, stack); stockpileBinaryNodeData(currentNode, arrayList, pointer); arrayList == NULL; currentNode == NULL; break; case ',': stockpileBinaryNodeData(currentNode, arrayList, pointer); left = 0; break; case ')': stockpileBinaryNodeData(currentNode, arrayList, pointer); stack->pop(stack); if (depth < probe)depth = probe; probe--; break; default: if (setBinaryNodeData(currentNode, arrayList, pointer)) break; currentNode = malloc(sizeof(struct BinaryNode)); if (currentNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return; } currentNode->data = NULL; currentNode->left = NULL; currentNode->right = NULL; setBinaryNodeData(currentNode, arrayList = initArrayList(5), pointer); if (treeRoot == NULL) { treeRoot = currentNode; } else { stackTopNode = (struct SequenceStack*)stack->top(stack); switch (left) { case 1: stackTopNode->left = currentNode; break; case 0: stackTopNode->right = currentNode; break; } } break; } index++; pointer = list[index]; } struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = treeRoot; binaryTree->depth = depth; return binaryTree; } int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* listBinaryTree = createListBinaryTree(list); drawBinaryTree(listBinaryTree); return 0; } |
运行结果:
A / \ B C / \ / \ * D G H / \ / \ / \ / \ * * E F * * * I / \ / \ / \ / \ / \ / \ / \ / \ * * * * J * * * * * * * * * K * / / / / / / / / / / / / / / / / \ |
2.完全二叉树--链表的算法
该算法的基本思想是:首先对一般的二叉树添加若干个虚结点,使其成为完全二叉树,第一个结点,则令其为根结点,然后依次设置结点信息,若设置的结点不是虚结点"*",则建立一个新结点,将新结点作为左孩子或右孩子链接到它的双亲结点上,如此重复下去,直到没有结点为止。
为了使新结点能正确地链接到其双亲结点上,使用为队列(3.3小节顺序队列)保存已记录的结点地址,使用数组(2.2小节动态数组)遍历索引指向当前访问结点。利用队列的队头指针front指向当前结点的双亲结点,利用数组索引指向当前结点。若数组索引为奇数,则说明当前结点应作为左孩子链接到font所指向的左结点上;否则,当前结点应作为右孩子链接到front所指向的右结点上;若当前结点为虚结点,则不需链接。每当数组索引遇到偶数索引,移除队头指针front指向的双亲结点。具体实现算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <Windows.h> #include "serialStack.h" #include "dynamicArray.h" #include "sequenceQueue.h" #define PRINT 1 #define EMPTY_NODE '*' struct BinaryNode { char* data; struct BinaryNode* left; struct BinaryNode* right; }; struct BinaryTree { struct BinaryNode* root; int depth; }; struct TreeArray { int seat; struct BinaryNode* node; }; struct TreeList { int size; struct TreeArray* treeArray; }; void linkChangeArray(struct BinaryNode* node, struct TreeArray* treeArray, int index) { if (node == NULL) { return; } treeArray[index].node = node; linkChangeArray(node->left, treeArray, index * 2 + 1); linkChangeArray(node->right, treeArray, index * 2 + 2); } int leftGetParent(int index) { return (index - 1) >> 1; } int rightGetParent(int index) { return (index - 2) >> 1; } int getLeftTree(int index) { return (index << 1) + 1; } int getRightTree(int index) { return (index << 1) + 2; } void markLeftIndex(struct TreeList* treeList, int index, int counter); void markRightIndex(struct TreeList* treeList, int index, int counter); void markParentIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0) { if (treeList->treeArray[index].seat == -1) { treeList->treeArray[index].seat = counter; markRightIndex(treeList, getRightTree(index), ++counter); } else { markParentIndex(treeList, leftGetParent(index), counter); } } } void markLeftIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } } void markRightIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, rightGetParent(index), ++counter); } } void markTreeIndex(struct TreeList* treeList, int index) { int counter = 1; treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } void printBinaryTree(struct TreeList* treeList) { int seat, size = treeList->size, front = 0, jump = 1, add = 2; for (int i = 0; i < size; i++) { seat = treeList->treeArray[i].seat - front; for (int j = 0; j < seat; j++) { printf(" "); front++; } if (treeList->treeArray[i].node == NULL) { printf("%c", EMPTY_NODE); } else { printf("%s", treeList->treeArray[i].node->data); } CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\\"); coord.X = ipBuffer.dwCursorPosition.X - 2; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("/"); coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); front++; if ((i + 1) % jump == 0) { CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\n"); jump = jump + add; add = add * 2; front = 0; } } } void drawBinaryTree(struct BinaryTree* binaryTree) { double size = floor(pow(2, binaryTree->depth)) - 1; struct TreeArray* treeArray = malloc(sizeof(struct TreeArray) * size); struct TreeList* treeList = malloc(sizeof(struct TreeList)); if (treeArray == NULL || treeList == NULL) { #if PRINT printf("绘制二叉树时。内存空间不足!\n"); #endif return; } for (int i = 0; i < size; i++) { treeArray[i].seat = -1; treeArray[i].node = NULL; } treeList->size = size; treeList->treeArray = treeArray; linkChangeArray(binaryTree->root, treeArray, 0); markTreeIndex(treeList, (int)size >> 1); printBinaryTree(treeList); } struct ArrayList* createArrayByBinaryTree(char* array) { int index = 0; char pointer = array[index]; struct ArrayList* arrayList = NULL; struct ArrayList* dataList = initArrayList(5); while (pointer != '\0') { arrayList = initArrayList(5); while (pointer != ' ' && pointer != '\0') { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) { #if PRINT printf("创建二叉树结点赋值失败,内存空间不足!\n"); #endif return; } arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; index++; pointer = array[index]; } char* data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList = NULL; dataList->insert(dataList->size(dataList), data, dataList); index++; pointer = array[index]; } return dataList; } struct BinaryTree* createBinaryTreeUseQueue(char* array) { struct ArrayList* dataList = createArrayByBinaryTree(array); struct SequenceQueue* queue = initSequenceQueue(); struct BinaryNode* rootNode = malloc(sizeof(struct BinaryNode)); if (rootNode == NULL) { #if PRINT printf("创建二叉树根结点失败,内存空间不足!\n"); #endif return; } rootNode->data = dataList->get(0, dataList); rootNode->left = NULL; rootNode->right = NULL; queue->push(rootNode, queue); int size = dataList->size(dataList); for (int i = 1; i < size; i++) { char* data = dataList->get(i, dataList); char* emptyNode = malloc(sizeof(char) * 2); if (emptyNode == NULL) { #if PRINT printf("创建二叉树空结点失败,内存空间不足!\n"); #endif return; } emptyNode[0] = EMPTY_NODE; emptyNode[1] = '\0'; if (strcmp(data, emptyNode) != 0) { struct BinaryNode* binaryNode = malloc(sizeof(struct BinaryNode)); if (binaryNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return; } binaryNode->data = data; binaryNode->left = NULL; binaryNode->right = NULL; queue->push(binaryNode, queue); struct BinaryNode* front = queue->front(queue); if (i % 2 == 0) { front->right = binaryNode; } else { front->left = binaryNode; } free(emptyNode); emptyNode == NULL; } else { queue->push(emptyNode, queue); } if (i % 2 == 0) { queue->pop(queue); } } struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = rootNode; binaryTree->depth = log2(size + 1); return binaryTree; } int main() { char* array = "A B C D E F G H I J K L M N O"; struct BinaryTree* arrayBinaryTree = createBinaryTreeUseQueue(array); drawBinaryTree(arrayBinaryTree); return 0; } |
运行结果:
A / \ B C / \ / \ D E F G / \ / \ / \ / \ H I J K L M N O / / / / / / / / \ |
还有一种算法,直接通过计算,计算二叉树双亲节点与孩子结点在数组(2.2小节动态数组)中的索引位置,然后利用数组的索引访问并创建二叉树链表结构。
双亲结点与孩子结点在数组中的位置计算方式为:双亲结点索引值的两倍加一的索引位置为其左孩子,双亲结点索引值的两倍加二的索引位置为其右孩子。
算法实现:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <Windows.h> #include "serialStack.h" #include "dynamicArray.h" #define PRINT 1 #define EMPTY_NODE '*' struct BinaryNode { char* data; struct BinaryNode* left; struct BinaryNode* right; }; struct BinaryTree { struct BinaryNode* root; int depth; }; struct TreeArray { int seat; struct BinaryNode* node; }; struct TreeList { int size; struct TreeArray* treeArray; }; void linkChangeArray(struct BinaryNode* node, struct TreeArray* treeArray, int index) { if (node == NULL) { return; } treeArray[index].node = node; linkChangeArray(node->left, treeArray, index * 2 + 1); linkChangeArray(node->right, treeArray, index * 2 + 2); } int leftGetParent(int index) { return (index - 1) >> 1; } int rightGetParent(int index) { return (index - 2) >> 1; } int getLeftTree(int index) { return (index << 1) + 1; } int getRightTree(int index) { return (index << 1) + 2; } void markLeftIndex(struct TreeList* treeList, int index, int counter); void markRightIndex(struct TreeList* treeList, int index, int counter); void markParentIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0) { if (treeList->treeArray[index].seat == -1) { treeList->treeArray[index].seat = counter; markRightIndex(treeList, getRightTree(index), ++counter); } else { markParentIndex(treeList, leftGetParent(index), counter); } } } void markLeftIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } } void markRightIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, rightGetParent(index), ++counter); } } void markTreeIndex(struct TreeList* treeList, int index) { int counter = 1; treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } void printBinaryTree(struct TreeList* treeList) { int seat, size = treeList->size, front = 0, jump = 1, add = 2; for (int i = 0; i < size; i++) { seat = treeList->treeArray[i].seat - front; for (int j = 0; j < seat; j++) { printf(" "); front++; } if (treeList->treeArray[i].node == NULL) { printf("%c", EMPTY_NODE); } else { printf("%s", treeList->treeArray[i].node->data); } CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\\"); coord.X = ipBuffer.dwCursorPosition.X - 2; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("/"); coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); front++; if ((i + 1) % jump == 0) { CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\n"); jump = jump + add; add = add * 2; front = 0; } } } void drawBinaryTree(struct BinaryTree* binaryTree) { double size = floor(pow(2, binaryTree->depth)) - 1; struct TreeArray* treeArray = malloc(sizeof(struct TreeArray) * size); struct TreeList* treeList = malloc(sizeof(struct TreeList)); if (treeArray == NULL || treeList == NULL) { #if PRINT printf("绘制二叉树时。内存空间不足!\n"); #endif return; } for (int i = 0; i < size; i++) { treeArray[i].seat = -1; treeArray[i].node = NULL; } treeList->size = size; treeList->treeArray = treeArray; linkChangeArray(binaryTree->root, treeArray, 0); markTreeIndex(treeList, (int)size >> 1); printBinaryTree(treeList); } struct ArrayList* createArrayByBinaryTree(char* array) { int index = 0; char pointer = array[index]; struct ArrayList* arrayList = NULL; struct ArrayList* dataList = initArrayList(5); while (pointer != '\0') { arrayList = initArrayList(5); while (pointer != ' ' && pointer != '\0') { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) { #if PRINT printf("创建二叉树结点赋值失败,内存空间不足!\n"); #endif return; } arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; index++; pointer = array[index]; } char* data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList = NULL; dataList->insert(dataList->size(dataList), data, dataList); index++; pointer = array[index]; } return dataList; } struct BinaryNode* createBinaryNodeByArray(struct ArrayList* dataList, int size, int index, struct BinaryNode** binaryNode) { if (index < size && index >= 0 && *binaryNode == NULL) { *binaryNode = malloc(sizeof(struct BinaryNode)); if (*binaryNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return NULL; } (*binaryNode)->data = dataList->get(index, dataList); (*binaryNode)->left = NULL; (*binaryNode)->right = NULL; createBinaryNodeByArray(dataList, size, getLeftTree(index), &(*binaryNode)->left); createBinaryNodeByArray(dataList, size, getRightTree(index), &(*binaryNode)->right); } } struct BinaryTree* createArrayBinaryTree(char* array) { struct ArrayList* dataList = createArrayByBinaryTree(array); struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = NULL; createBinaryNodeByArray(dataList, dataList->size(dataList), 0, &binaryTree->root); binaryTree->depth = log2(dataList->size(dataList) + 1); return binaryTree; } int main() { char* array = "A B C D E F G H I J K L M N O"; struct BinaryTree* arrayBinaryTree = createArrayBinaryTree(array); drawBinaryTree(arrayBinaryTree); return 0; } |
运行结果:
A / \ B C / \ / \ D E F G / \ / \ / \ / \ H I J K L M N O / / / / / / / / \ |
3.封装二叉树生成算法
将上述构建二叉树的算法封装起来以便日后复用,创建binaryTree.h头文件:
#pragma once #ifndef BINARY_TREE_H #define BINARY_TREE_H struct BinaryTree { struct BinaryNode* root; int depth; void (*draw)(struct BinaryTree* binaryTree); }; struct BinaryNode { char* data; struct BinaryNode* left; struct BinaryNode* right; }; struct BinaryTree* initBinaryTree(char* arrayOrList, int listOrArray); #endif |
实现头文件逻辑binaryTree.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <Windows.h> #include "binaryTree.h" #include "serialStack.h" #include "dynamicArray.h" #include "sequenceQueue.h" #define PRINT 1 #define EMPTY_NODE '*' void drawBinaryTree(struct BinaryTree* binaryTree); struct BinaryTree* createListBinaryTree(char* list); struct BinaryTree* createArrayBinaryTree(char* array); struct BinaryTree* createBinaryTreeUseQueue(char* array); struct BinaryTree* initBinaryTree(char* arrayOrList, int listOrArray) { struct BinaryTree* binaryTree = NULL; if (listOrArray == 2) { binaryTree = createBinaryTreeUseQueue(arrayOrList); } else if (listOrArray) { binaryTree = createListBinaryTree(arrayOrList); } else { binaryTree = createArrayBinaryTree(arrayOrList); } binaryTree->draw = drawBinaryTree; return binaryTree; } struct TreeArray { int seat; struct BinaryNode* node; }; struct TreeList { int size; struct TreeArray* treeArray; }; void linkChangeArray(struct BinaryNode* node, struct TreeArray* treeArray, int index) { if (node == NULL) { return; } treeArray[index].node = node; linkChangeArray(node->left, treeArray, index * 2 + 1); linkChangeArray(node->right, treeArray, index * 2 + 2); } int leftGetParent(int index) { return (index - 1) >> 1; } int rightGetParent(int index) { return (index - 2) >> 1; } int getLeftTree(int index) { return (index << 1) + 1; } int getRightTree(int index) { return (index << 1) + 2; } void markLeftIndex(struct TreeList* treeList, int index, int counter); void markRightIndex(struct TreeList* treeList, int index, int counter); void markParentIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0) { if (treeList->treeArray[index].seat == -1) { treeList->treeArray[index].seat = counter; markRightIndex(treeList, getRightTree(index), ++counter); } else { markParentIndex(treeList, leftGetParent(index), counter); } } } void markLeftIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } } void markRightIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, rightGetParent(index), ++counter); } } void markTreeIndex(struct TreeList* treeList, int index) { int counter = 1; treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } void printBinaryTree(struct TreeList* treeList) { int seat, size = treeList->size, front = 0, jump = 1, add = 2; for (int i = 0; i < size; i++) { seat = treeList->treeArray[i].seat - front; for (int j = 0; j < seat; j++) { printf(" "); front++; } if (treeList->treeArray[i].node == NULL) { printf("%c", EMPTY_NODE); } else { printf("%s", treeList->treeArray[i].node->data); } CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\\"); coord.X = ipBuffer.dwCursorPosition.X - 2; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("/"); coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); front++; if ((i + 1) % jump == 0) { CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\n"); jump = jump + add; add = add * 2; front = 0; } } } void drawBinaryTree(struct BinaryTree* binaryTree) { double size = floor(pow(2, binaryTree->depth)) - 1; struct TreeArray* treeArray = malloc(sizeof(struct TreeArray) * size); struct TreeList* treeList = malloc(sizeof(struct TreeList)); if (treeArray == NULL || treeList == NULL) { #if PRINT printf("绘制二叉树时。内存空间不足!\n"); #endif return; } for (int i = 0; i < size; i++) { treeArray[i].seat = -1; treeArray[i].node = NULL; } treeList->size = size; treeList->treeArray = treeArray; linkChangeArray(binaryTree->root, treeArray, 0); markTreeIndex(treeList, (int)size >> 1); printBinaryTree(treeList); } void stockpileBinaryNodeData(struct BinaryNode* currentNode, struct ArrayList* arrayList, char pointer) { if (currentNode != NULL && currentNode->data == NULL && arrayList != NULL) { currentNode->data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList == NULL; currentNode == NULL; } } int setBinaryNodeData(struct BinaryNode* currentNode, struct ArrayList* arrayList, char pointer) { if (currentNode != NULL && currentNode->data == NULL && arrayList != NULL) { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) { #if PRINT printf("创建二叉树结点赋值失败,内存空间不足!\n"); #endif return; } arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; return 1; } return 0; } struct BinaryTree* createListBinaryTree(char* list) { int index = 0, left = 1, probe = 0, depth = probe; char pointer = list[index]; struct SequenceStack* stack = initSequenceStack(); struct BinaryNode* currentNode = NULL; struct BinaryNode* stackTopNode = NULL; struct BinaryNode* treeRoot = NULL; struct ArrayList* arrayList = NULL; while (pointer != '\0') { if (pointer == ' ') { index++; pointer = list[index]; continue; } switch (pointer) { case '(': left = 1; probe++; stack->push(currentNode, stack); stockpileBinaryNodeData(currentNode, arrayList, pointer); arrayList == NULL; currentNode == NULL; break; case ',': stockpileBinaryNodeData(currentNode, arrayList, pointer); left = 0; break; case ')': stockpileBinaryNodeData(currentNode, arrayList, pointer); stack->pop(stack); if (depth < probe)depth = probe; probe--; break; default: if (setBinaryNodeData(currentNode, arrayList, pointer)) break; currentNode = malloc(sizeof(struct BinaryNode)); if (currentNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return; } currentNode->data = NULL; currentNode->left = NULL; currentNode->right = NULL; setBinaryNodeData(currentNode, arrayList = initArrayList(5), pointer); if (treeRoot == NULL) { treeRoot = currentNode; } else { stackTopNode = (struct SequenceStack*)stack->top(stack); switch (left) { case 1: stackTopNode->left = currentNode; break; case 0: stackTopNode->right = currentNode; break; } } break; } index++; pointer = list[index]; } struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = treeRoot; binaryTree->depth = depth; return binaryTree; } struct ArrayList* createArrayByBinaryTree(char* array) { int index = 0; char pointer = array[index]; struct ArrayList* arrayList = NULL; struct ArrayList* dataList = initArrayList(5); while (pointer != '\0') { arrayList = initArrayList(5); while (pointer != ' ' && pointer != '\0') { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) { #if PRINT printf("创建二叉树结点赋值失败,内存空间不足!\n"); #endif return; } arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; index++; pointer = array[index]; } char* data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList = NULL; dataList->insert(dataList->size(dataList), data, dataList); index++; pointer = array[index]; } return dataList; } struct BinaryNode* createBinaryNodeByArray(struct ArrayList* dataList, int size, int index, struct BinaryNode** binaryNode) { if (index < size && index >= 0 && *binaryNode == NULL) { *binaryNode = malloc(sizeof(struct BinaryNode)); if (*binaryNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return NULL; } (*binaryNode)->data = dataList->get(index, dataList); (*binaryNode)->left = NULL; (*binaryNode)->right = NULL; createBinaryNodeByArray(dataList, size, getLeftTree(index), &(*binaryNode)->left); createBinaryNodeByArray(dataList, size, getRightTree(index), &(*binaryNode)->right); } } struct BinaryTree* createArrayBinaryTree(char* array) { struct ArrayList* dataList = createArrayByBinaryTree(array); struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = NULL; createBinaryNodeByArray(dataList, dataList->size(dataList), 0, &binaryTree->root); binaryTree->depth = log2(dataList->size(dataList) + 1); return binaryTree; } struct BinaryTree* createBinaryTreeUseQueue(char* array) { struct ArrayList* dataList = createArrayByBinaryTree(array); struct SequenceQueue* queue = initSequenceQueue(); struct BinaryNode* rootNode = malloc(sizeof(struct BinaryNode)); if (rootNode == NULL) { #if PRINT printf("创建二叉树根结点失败,内存空间不足!\n"); #endif return; } rootNode->data = dataList->get(0, dataList); rootNode->left = NULL; rootNode->right = NULL; queue->push(rootNode, queue); int size = dataList->size(dataList); for (int i = 1; i < size; i++) { char* data = dataList->get(i, dataList); char* emptyNode = malloc(sizeof(char) * 2); if (emptyNode == NULL) { #if PRINT printf("创建二叉树空结点失败,内存空间不足!\n"); #endif return; } emptyNode[0] = EMPTY_NODE; emptyNode[1] = '\0'; if (strcmp(data, emptyNode) != 0) { struct BinaryNode* binaryNode = malloc(sizeof(struct BinaryNode)); if (binaryNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return; } binaryNode->data = data; binaryNode->left = NULL; binaryNode->right = NULL; queue->push(binaryNode, queue); struct BinaryNode* front = queue->front(queue); if (i % 2 == 0) { front->right = binaryNode; } else { front->left = binaryNode; } free(emptyNode); emptyNode == NULL; } else { queue->push(emptyNode, queue); } if (i % 2 == 0) { queue->pop(queue); } } struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = rootNode; binaryTree->depth = log2(size + 1); return binaryTree; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* listBinaryTree = initBinaryTree(list, 1); listBinaryTree->draw(listBinaryTree); char* array = "A B C D E F G H I J K L M N O"; struct BinaryTree* arrayBinaryTree = initBinaryTree(array, 0); arrayBinaryTree->draw(arrayBinaryTree); char* queue = "A B C D E F G H I J K L M N O"; struct BinaryTree* queueBinaryTree = initBinaryTree(queue, 2); queueBinaryTree->draw(queueBinaryTree); return 0; } |
测试结果:
A / \ B C / \ / \ * D G H / \ / \ / \ / \ * * E F * * * I / \ / \ / \ / \ / \ / \ / \ / \ * * * * J * * * * * * * * * K * / / / / / / / / / / / / / / / / \ A / \ B C / \ / \ D E F G / \ / \ / \ / \ H I J K L M N O / / / / / / / / \ A / \ B C / \ / \ D E F G / \ / \ / \ / \ H I J K L M N O / / / / / / / / \ |
5.3.2.二叉树遍历
在二叉树的应用中,遍历二叉树是一种最重要的运算,是二叉树中所有其他运算的基础。所谓遍历,是指沿着某条搜索路径(线)周游二叉树,依次对树中每个结点访问且仅访问一次。遍历对于一般的线性结构来说是一个很容易解决的问题,只需要从开始结点出发,依次访问当前结点的直接后继,直到终端结点为止。然而,对于二又树而言,树中每个结点都可能有两个后继结点,这将导致存在多条遍历路线。因此,需要寻找一种规律,以便系统地访问树中各个结点。
1.递归遍历
根据二叉树的递归定义,遍历一棵非空二叉树的问题可分解为三个子问题:访问根结点、遍历左子树和遍历右子树。若分别用D、L和R表示以上三个问题,则有DLR、LDR、LRD和DRL、RDL、RLD六种次序遍历方案。其中,前三种方案是按先左后右的次序遍历根的两棵子树,而后三种则是按先右后左的次序遍历两棵子树。由于两者对称,因此这里仅讨论前三种次序的遍历方案。
在遍历方案DLR中,因为访问根结点的操作在遍历左、右子树之前,故称之为前序(Preorder)遍历或先根遍历;类似地,在方案LDR中,访问根结点的操作在遍历左子树之后和遍历右子树之前,故称之为中序(Inorder)遍历或中根遍历;在方案LRD中,因为访问根结点的操作在遍历左、右子树之后,故称之为后序(Postorder)遍历或后根遍历。显然,遍历左、右子树的问题仍然是遍历二叉树的问题,当二叉树为空时递归遍历结束,所以很容易给出以上三种遍历的递归算法定义。
(1)前序遍历二叉树的递归定义
若二叉树非空,则依次进行操作: ①访问根结点; ②前序遍历左子树; ③前序遍历右子树。
(2)中序遍历三叉树的递归定义
若二叉树非空,则依次进行操作: ①中序遍历左子树; ②访问根结点; ③中序遍历右子树。
(3)后序遍历二叉树的递归定义
若二叉树非空,则依次进行操作: ①后序遍历左子树; ②后序遍历右子树; ③访问根结点。
在有了上述遍历二叉树的递归定义描述之后,三种遍历的算法就很容易实现。遍历算法中的递归终止条件是二叉树为空。用C语言描述的前序遍历的递归算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" void preorderTraversal(struct BinaryNode* node) { if (node == NULL) { return; } printf("%s ", node->data); preorderTraversal(node->left); preorderTraversal(node->right); } void inorderTraversal(struct BinaryNode* node) { if (node == NULL) { return; } inorderTraversal(node->left); printf("%s ", node->data); inorderTraversal(node->right); } void subsequentTraversal(struct BinaryNode* node) { if (node == NULL) { return; } subsequentTraversal(node->left); subsequentTraversal(node->right); printf("%s ", node->data); } void recursionTree(struct BinaryNode* node) { printf("先序:"); preorderTraversal(node); printf("\n中序:"); inorderTraversal(node); printf("\n后序:"); subsequentTraversal(node); printf("\n"); } int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* listBinaryTree = initBinaryTree(list, 1); recursionTree(listBinaryTree->root); return 0; } |
运行结果:
先序:A B D E J F C G H I K 中序:B J E D F A G C H K I 后序:J E F D B G K I H C A |
为了便于理解递归算法,现以图5.10所示的二叉树以及该二叉树对应的二叉链表为上述算法遍历二叉树的执行过程。
void preorderTraversal(struct BinaryNode* node) ① { if (node == NULL) ⑤ { return; } printf("%s ", node->data); ② preorderTraversal(node->left); ③ preorderTraversal(node->right); ④ } |
为了叙述方便,在二叉链表的每个结点左边标上一个序号,假设为该结点的存储地址。
当一个函数调用前序遍历算法时,先将指向二叉树根结点的地址实参1,传递给算法中的形参node,然后执行算法:
1. 执行算法,②先打印出node结点存储的数据;
2. 然后③遍历左字树,将的左子树地址2递归传递给算法形参node,回到第1步执行算法;
3. 此时的右子树并没有得到及时的遍历,所以系统要记住此时右子树的访问地址④,等左子树遍历结束后再回来遍历右子树④。遍历右子树,将右子树地址3递归传递给算法形参node,回到第1步执行算法;
4. 当接收的参数node为空时,⑤结束执行函数,递归回到上一步的函数调用者(可能是③、④或者是根结点A),继续执行函数逻辑。
2.非递归遍历
依照递归算法执行过程中递归工作栈的状态变化,很容易写出相应的非递归算法。
利用栈容器可以实现二叉树的非递归遍历,首先将每个节点都设置一个标志,默认标志为假,根据节点的的状态进行如下流程。
1.将根结点压入栈中。
2.进入循环:只要栈中元素个数大于0,进行循环操作。
• 弹出栈顶元素;
• 如果这个栈顶元素标志为真,输出这个元素,并且执行下一次循环;
• 如果栈顶元素标志为假,将结点的标志设置为真;
• ❌将该结点的右子树、左子树、以及该结点压入栈中;
• 执行下一次循环。
执行上述流程,可以得到前序遍历的结果,如果想得到其他二叉树遍历结果,修改❌号步骤即可。
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" #include "serialStack.h" struct BinaryNodeStack { char* data; struct BinaryNodeStack* left; struct BinaryNodeStack* right; int flag; }; void foreachBinaryTree(struct BinaryNodeStack* node) { struct SequenceStack* stack = initSequenceStack(); stack->push(node, stack); while (stack->size(stack) > 0) { struct BinaryNodeStack* currentNode = stack->top(stack); stack->pop(stack); if (currentNode->flag == 0) { currentNode->flag = 1; if (currentNode->right != NULL) { stack->push(currentNode->right, stack); } if (currentNode->left != NULL) { stack->push(currentNode->left, stack); } stack->push(currentNode, stack); continue; } printf("%s ", currentNode->data); } stack->destroy(stack); } struct BinaryNodeStack* copyTree(struct BinaryNode* node) { if (node == NULL) { return NULL; } struct BinaryNode* left = copyTree(node->left); struct BinaryNode* right = copyTree(node->right); struct BinaryNodeStack* treeNode = malloc(sizeof(struct BinaryNodeStack)); if (treeNode == NULL) { return NULL; } treeNode->data = node->data; treeNode->left = left; treeNode->right = right; treeNode->flag = 0; return treeNode; } int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* listBinaryTree = initBinaryTree(list, 1); struct BinaryNodeStack* node = copyTree(listBinaryTree->root); foreachBinaryTree(node); return 0; } |
运行结果:
A B D E J F C G H I K |
3.层序遍历
非递归的按层遍历二叉链表树。其算法思想是:采用一队列Q,若树不空,先将二叉树根结点输出,并将根结点指针入队,然后出队。若根结点有左子树,则将左子树的根结点输出并将其指针入队;若其有右子树,则将其右子树的根结点输出并将其指针入队,再出队,如此下去,直至队列空为止。因此,实现要求的算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" #include "sequenceQueue.h" void foreachTier(struct BinaryNode* node) { struct SequenceQueue* queue = initSequenceQueue(); queue->push(node, queue); while (queue->isNotEmpty(queue)) { struct BinaryNode* currentNode = queue->front(queue); printf("%s ", currentNode->data); if (currentNode->left != NULL) queue->push(currentNode->left, queue); if (currentNode->right != NULL) queue->push(currentNode->right, queue); queue->pop(queue); } } int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* listBinaryTree = initBinaryTree(list, 1); listBinaryTree->draw(listBinaryTree); foreachTier(listBinaryTree->root); return 0; } |
运行结果:
A / \ B C / \ / \ * D G H / \ / \ / \ / \ * * E F * * * I / \ / \ / \ / \ / \ / \ / \ / \ * * * * J * * * * * * * * * K * / / / / / / / / / / / / / / / / \ A B C D G H E F I J K |
【例5.1】分别写出如图5.12所示的二叉树的前、中、后序遍历序列。
解:按照前面介绍的三种递归或非递归的遍历二叉树算法,很容易给出遍历序列。其中,前序序列为ABDHEICFG,中序序列为DHBEIAFCG,后序序列为HDIEBFGCA
5.3.3.二叉树应用
【例5.2】已知二叉树的前序和中序遍历序列或中序和后序遍历序列,求其二叉树。
分析:根据二叉树的三种遍历算法可以得出这样一个结论:已知一棵三叉树的前序和中序遍历序列或中序和后序遍历序列,可唯一地确定一棵二叉树。具体方法如下:
(1)根据前序或后序遍历序列确定二叉树的各子树的根;
(2)根据中序遍历序列确定各子树根的左、右子树。
例如,一棵二叉树的前序和中序遍历序列分别为ABDEGHCFI和DBGEHACIF,要求出其后序遍历序列,就必须求出其二叉树。
求解过程如下:
(1)由前序遍历序列确定二叉树的根为A,再由中序遍历序列确定A的左、右子树。
A BDEGH CFI //前序遍历序列的根、左子树个右子树
DBGEH A CIF //中序遍历序列的左子树、根和右子树
(2)确定A的左子树
B D EGH //前序遍历左子树的根、左子树和右子树
D B GEH //中序遍历左子树的左子树、根和右子树
- 再确定B的右子树:由前序序列EGH和中序序列GEH唯一确定E为根,G、H分为左子树和右子树。
- 确定A的右子树。
C FI //前序遍历A右子树的根和右子树
C IF //中序遍历A右子树的根和右子树
- 再确定C的右子树。由前序FI和中序IF确定F为根,I为左子树。
由此可得所求二叉树如图5.13所示。因此,该二叉树的后序遍历序列为:DGHEBIFCA。
算法实现:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" #include "dynamicArray.h" struct ArrayList* createArrayByBinary(char* array) { int index = 0; char pointer = array[index]; struct ArrayList* arrayList = NULL; struct ArrayList* dataList = initArrayList(5); while (pointer != '\0') { arrayList = initArrayList(5); while (pointer != ' ' && pointer != '\0') { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) return; arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; index++; pointer = array[index]; } char* data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList = NULL; dataList->insert(dataList->size(dataList), data, dataList); index++; pointer = array[index]; } return dataList; } static struct SortArray { char* data; int index; }; void sortPreorderArray(struct SortArray* array, int inorderSize) { int flag = 0; struct SortArray* temp = malloc(sizeof(struct SortArray)); for (int i = 0; i < inorderSize; i++) { for (int j = 0; j < inorderSize - 1 - i; j++) { if (array[j].index > array[j + 1].index) { flag = 0; temp->data = array[j].data; temp->index = array[j].index; array[j].data = array[j + 1].data; array[j].index = array[j + 1].index; array[j + 1].data = temp->data; array[j + 1].index = temp->index; } else { flag++; } } if (flag == (inorderSize - 1 - i)) { break; } flag = 0; } free(temp); temp = NULL; } struct ArrayList* convertPreorder(struct ArrayList* preorder, struct ArrayList* referTo) { int preorderSize = preorder->size(preorder); int referToSize = referTo->size(referTo); struct SortArray* array = malloc(sizeof(struct SortArray) * referToSize); if (array == NULL) { return NULL; } for (int i = 0; i < referToSize; i++) { for (int j = 0; j < preorderSize; j++) { if (strcmp(referTo->get(i, referTo), preorder->get(j, preorder)) == 0) { array[i].data = preorder->get(j, preorder); array[i].index = j; continue; } } } sortPreorderArray(array, referToSize); struct ArrayList* preorderList = initArrayList(5); for (int i = 0; i < referToSize; i++) { preorderList->insert(i, array[i].data, preorderList); } free(array); array = NULL; return preorderList; } struct BinaryNode* createNodeByPreorderAndInorder(struct ArrayList* preorder, struct ArrayList* inorder, int* depth, int probe) { if (preorder->size(preorder) <= 0 || inorder->size(inorder) <= 0) { return NULL; } char* parentData = preorder->get(0, preorder); struct BinaryNode* binaryNode = malloc(sizeof(struct BinaryNode)); if (binaryNode == NULL) { return; } binaryNode->data = parentData; binaryNode->left = NULL; binaryNode->right = NULL; if (preorder->size(preorder) == 1 || inorder->size(inorder) == 1) { if (*depth < probe) *depth = probe; return binaryNode; } // find parent node index by inorder. int size = inorder->size(inorder), index = 0; for (int i = 0; i < size; i++) { if (strcmp(inorder->get(i, inorder), parentData) == 0) { index = i; break; } } struct ArrayList* leftInorder = initArrayList(5); inorder->copy(inorder, leftInorder, 0, index); binaryNode->left = createNodeByPreorderAndInorder(convertPreorder(preorder, leftInorder), leftInorder, depth, ++probe); leftInorder->destroy(leftInorder); probe--; struct ArrayList* rightInorder = initArrayList(5); inorder->copy(inorder, rightInorder, index + 1, -1); binaryNode->right = createNodeByPreorderAndInorder(convertPreorder(preorder, rightInorder), rightInorder, depth, ++probe); rightInorder->destroy(rightInorder); return binaryNode; } struct BinaryTree* createTreeByPreorderAndInorder(char* preorderArray, char* inorderArray) { struct ArrayList* preorder = createArrayByBinary(preorderArray); struct ArrayList* inorder = createArrayByBinary(inorderArray); int* depth = malloc(sizeof(int)); if (depth == NULL) { return NULL; } *depth = 0; struct BinaryNode* binaryNode = createNodeByPreorderAndInorder(preorder, inorder, depth, 1); preorder->destroy(preorder); inorder->destroy(inorder); struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { return NULL; } binaryTree->root = binaryNode; binaryTree->depth = *depth; free(depth); depth = NULL; return binaryTree; } int main() { char preorder[19] = "A B D E G H C F I"; char inorder[19] = "D B G E H A C I F"; struct BinaryTree* tree = createTreeByPreorderAndInorder(preorder, inorder); struct BinaryTree* binaryTree = initBinaryTree("", 2); binaryTree->root = tree->root; binaryTree->depth = tree->depth; binaryTree->draw(binaryTree); return 0; } |
运行结果:
A / \ B C / \ / \ D E * F / \ / \ / \ / \ * * G H * * I * / / / / / / / / \ |
【例5.3】已知二叉树的链式存储结构,求二叉树的深度。
分析:若一棵二叉树为空,则它的深度为0,否则它的深度等于其左右子树中的最大深度加1。设depl和depr分别表示左右子树的深度,则二叉树的深度为:
max(dep1,depr)+1
因此,求二叉树深度的递归算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" int depth(struct BinaryNode* node) { if (node == NULL) { return 0; } int left = depth(node->left); int right = depth(node->right); return left > right ? left + 1 : right + 1; } int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* listBinaryTree = initBinaryTree(list, 1);
printf("深度:%d", depth(listBinaryTree->root)); return 0; } |
【例5.4】 以二叉链表为存储结构,试编写在二叉树中查找值为x的结点及求x所在结点在树中层数的算法。
分析: (1)按值查找。该算法是比较简单的,无论是利用三种遍历算法的哪一种,都很容易实现,不妨用前序遍历算法。
(2)求结点的层次。依照题意,仍然采用递归算法。从根节点开始递归遍历,每递归一层加一,回退一层减一,匹配到数值则打印出来。因此,实现算法如下:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" int getBinaryNode(struct BinaryNode* binaryNode, char* data, int tier) { if (binaryNode != NULL && tier > 0) { if (strcmp(binaryNode->data, data) == 0) { printf("%s %d\n", binaryNode->data, tier); tier = 0; } else { tier = getBinaryNode(binaryNode->left, data, ++tier); --tier; tier = getBinaryNode(binaryNode->right, data, ++tier); } } return tier; } int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* binaryTree = initBinaryTree(list, 1); getBinaryNode(binaryTree->root, "C", 1); getBinaryNode(binaryTree->root, "J", 1); getBinaryNode(binaryTree->root, "H", 1); return 0; } |
运行结果:
C 2 J 5 H 3 |
5.3.4.二叉树生成算法与应用
binaryTree.h头文件:
#pragma once #ifndef BINARY_TREE_H #define BINARY_TREE_H #include "serialStack.h" #include "dynamicArray.h" #include "sequenceQueue.h" enum Sequence { preorder = 1, inorder = 2, subsequent = 3 }; struct BinaryTree { struct BinaryNode* root; int depth; void (*draw)(struct BinaryTree* binaryTree); void (*foreachUseRecursion)(struct BinaryTree* binaryTree, enum Sequence sequence); void (*foreachUseStack)(struct BinaryTree* binaryTree, enum Sequence sequence); void (*foreachTier)(struct BinaryTree* binaryTree); }; struct BinaryNode { char* data; struct BinaryNode* left; struct BinaryNode* right; }; struct BinaryTree* initBinaryTree(char* arrayOrList, int listOrArray); struct BinaryTree* initTreeBySequenceAndInorder(char* sequenceArray, char* inorderArray, int foreOrHind); #endif |
实现头文件逻辑binaryTree.c:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <Windows.h> #include "binaryTree.h" #define PRINT 1 #define EMPTY_NODE '*' void drawBinaryTree(struct BinaryTree* binaryTree); struct BinaryTree* createListBinaryTree(char* list); struct BinaryTree* createArrayBinaryTree(char* array); struct BinaryTree* createBinaryTreeUseQueue(char* array); void foreachRecursionBinaryTree(struct BinaryTree* binaryTree, enum Sequence sequence); void foreachStackBinaryTree(struct BinaryTree* binaryTree, enum Sequence sequence); void foreachBinaryTreeTier(struct BinaryTree* binaryTree); void setBinaryTreeFunction(struct BinaryTree* binaryTree) { binaryTree->draw = drawBinaryTree; binaryTree->foreachUseRecursion = foreachRecursionBinaryTree; binaryTree->foreachUseStack = foreachStackBinaryTree; binaryTree->foreachTier = foreachBinaryTreeTier; } struct BinaryTree* initBinaryTree(char* arrayOrList, int listOrArray) { struct BinaryTree* binaryTree = NULL; if (listOrArray == 2) { binaryTree = createBinaryTreeUseQueue(arrayOrList); } else if (listOrArray) { binaryTree = createListBinaryTree(arrayOrList); } else { binaryTree = createArrayBinaryTree(arrayOrList); } setBinaryTreeFunction(binaryTree); return binaryTree; } struct TreeArray { int seat; struct BinaryNode* node; }; struct TreeList { int size; struct TreeArray* treeArray; }; void linkChangeArray(struct BinaryNode* binaryNode, struct TreeArray* treeArray, int index) { if (binaryNode == NULL) { return; } treeArray[index].node = binaryNode; linkChangeArray(binaryNode->left, treeArray, index * 2 + 1); linkChangeArray(binaryNode->right, treeArray, index * 2 + 2); } int leftGetParent(int index) { return (index - 1) >> 1; } int rightGetParent(int index) { return (index - 2) >> 1; } int getLeftTree(int index) { return (index << 1) + 1; } int getRightTree(int index) { return (index << 1) + 2; } void markLeftIndex(struct TreeList* treeList, int index, int counter); void markRightIndex(struct TreeList* treeList, int index, int counter); void markParentIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0) { if (treeList->treeArray[index].seat == -1) { treeList->treeArray[index].seat = counter; markRightIndex(treeList, getRightTree(index), ++counter); } else { markParentIndex(treeList, leftGetParent(index), counter); } } } void markLeftIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } } void markRightIndex(struct TreeList* treeList, int index, int counter) { if (index < treeList->size && index >= 0 && treeList->treeArray[index].seat == -1) { if (getLeftTree(index) <= treeList->size) { markLeftIndex(treeList, getLeftTree(index), counter); return; } treeList->treeArray[index].seat = counter; markParentIndex(treeList, rightGetParent(index), ++counter); } } void markTreeIndex(struct TreeList* treeList, int index) { int counter = 1; treeList->treeArray[index].seat = counter; markParentIndex(treeList, leftGetParent(index), ++counter); } void printBinaryTree(struct TreeList* treeList) { int seat, size = treeList->size, front = 0, jump = 1, add = 2; for (int i = 0; i < size; i++) { seat = treeList->treeArray[i].seat - front; for (int j = 0; j < seat; j++) { printf(" "); front++; } if (treeList->treeArray[i].node == NULL) { printf("%c", EMPTY_NODE); } else { printf("%s", treeList->treeArray[i].node->data); } CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\\"); coord.X = ipBuffer.dwCursorPosition.X - 2; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("/"); coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); front++; if ((i + 1) % jump == 0) { CONSOLE_SCREEN_BUFFER_INFO ipBuffer; GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ipBuffer); COORD coord = { 0 }; coord.X = ipBuffer.dwCursorPosition.X; coord.Y = ipBuffer.dwCursorPosition.Y + 1; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); printf("\n"); jump = jump + add; add = add * 2; front = 0; } } } void drawBinaryTree(struct BinaryTree* binaryTree) { int depth = binaryTree->depth; if (depth <= 0) { #if PRINT printf("空二叉树无法绘制!\n"); #endif return; } double size = floor(pow(2, depth)) - 1; struct TreeArray* treeArray = malloc(sizeof(struct TreeArray) * size); struct TreeList* treeList = malloc(sizeof(struct TreeList)); if (treeArray == NULL || treeList == NULL) { #if PRINT printf("绘制二叉树时。内存空间不足!\n"); #endif return; } for (int i = 0; i < size; i++) { treeArray[i].seat = -1; treeArray[i].node = NULL; } treeList->size = size; treeList->treeArray = treeArray; linkChangeArray(binaryTree->root, treeArray, 0); markTreeIndex(treeList, (int)size >> 1); printBinaryTree(treeList); free(treeArray); treeArray = NULL; free(treeList); treeList = NULL; } void stockpileBinaryNodeData(struct BinaryNode* currentNode, struct ArrayList* arrayList, char pointer) { if (currentNode != NULL && currentNode->data == NULL && arrayList != NULL) { currentNode->data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList == NULL; currentNode == NULL; } } int setBinaryNodeData(struct BinaryNode* currentNode, struct ArrayList* arrayList, char pointer) { if (currentNode != NULL && currentNode->data == NULL && arrayList != NULL) { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) { #if PRINT printf("创建二叉树结点赋值失败,内存空间不足!\n"); #endif return; } arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; return 1; } return 0; } struct BinaryTree* createListBinaryTree(char* list) { int index = 0, left = 1, probe = 0, depth = probe; char pointer = list[index]; struct SequenceStack* stack = initSequenceStack(); struct BinaryNode* currentNode = NULL; struct BinaryNode* stackTopNode = NULL; struct BinaryNode* treeRoot = NULL; struct ArrayList* arrayList = NULL; while (pointer != '\0') { if (pointer == ' ') { index++; pointer = list[index]; continue; } switch (pointer) { case '(': left = 1; probe++; stack->push(currentNode, stack); stockpileBinaryNodeData(currentNode, arrayList, pointer); arrayList == NULL; currentNode == NULL; break; case ',': stockpileBinaryNodeData(currentNode, arrayList, pointer); left = 0; break; case ')': stockpileBinaryNodeData(currentNode, arrayList, pointer); stack->pop(stack); if (depth < probe)depth = probe; probe--; break; default: if (setBinaryNodeData(currentNode, arrayList, pointer)) break; currentNode = malloc(sizeof(struct BinaryNode)); if (currentNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return; } currentNode->data = NULL; currentNode->left = NULL; currentNode->right = NULL; setBinaryNodeData(currentNode, arrayList = initArrayList(5), pointer); if (treeRoot == NULL) { treeRoot = currentNode; } else { stackTopNode = (struct SequenceStack*)stack->top(stack); switch (left) { case 1: stackTopNode->left = currentNode; break; case 0: stackTopNode->right = currentNode; break; } } break; } index++; pointer = list[index]; } stack->destroy(stack); struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = treeRoot; binaryTree->depth = depth; return binaryTree; } struct ArrayList* createArrayByBinaryTree(char* array) { int index = 0; char pointer = array[index]; struct ArrayList* arrayList = NULL; struct ArrayList* dataList = initArrayList(5); while (pointer != '\0') { arrayList = initArrayList(5); while (pointer != ' ' && pointer != '\0') { char* data = malloc(sizeof(char)); if (data == NULL || arrayList == NULL) { #if PRINT printf("创建二叉树结点赋值失败,内存空间不足!\n"); #endif return; } arrayList->insert(arrayList->size(arrayList), data, arrayList); *data = pointer; index++; pointer = array[index]; } char* data = arrayList->toString(arrayList); arrayList->ruin(arrayList); arrayList = NULL; dataList->insert(dataList->size(dataList), data, dataList); index++; pointer = array[index]; } return dataList; } struct BinaryNode* createBinaryNodeByArray(struct ArrayList* dataList, int size, int index, struct BinaryNode** binaryNode) { if (index < size && index >= 0 && *binaryNode == NULL) { *binaryNode = malloc(sizeof(struct BinaryNode)); if (*binaryNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return NULL; } (*binaryNode)->data = dataList->get(index, dataList); (*binaryNode)->left = NULL; (*binaryNode)->right = NULL; createBinaryNodeByArray(dataList, size, getLeftTree(index), &(*binaryNode)->left); createBinaryNodeByArray(dataList, size, getRightTree(index), &(*binaryNode)->right); } } struct BinaryTree* createArrayBinaryTree(char* array) { struct ArrayList* dataList = createArrayByBinaryTree(array); struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = NULL; createBinaryNodeByArray(dataList, dataList->size(dataList), 0, &binaryTree->root); binaryTree->depth = log2(dataList->size(dataList) + 1); dataList->destroy(dataList); return binaryTree; } struct BinaryTree* createBinaryTreeUseQueue(char* array) { struct ArrayList* dataList = createArrayByBinaryTree(array);
struct BinaryNode* rootNode = malloc(sizeof(struct BinaryNode)); if (rootNode == NULL) { #if PRINT printf("创建二叉树根结点失败,内存空间不足!\n"); #endif return; } rootNode->data = NULL; rootNode->left = NULL; rootNode->right = NULL; int size = dataList->size(dataList); if (size > 0) { struct SequenceQueue* queue = initSequenceQueue(); rootNode->data = dataList->get(0, dataList); queue->push(rootNode, queue); char* emptyNode = malloc(sizeof(char) * 2); if (emptyNode == NULL) { #if PRINT printf("创建二叉树空结点失败,内存空间不足!\n"); #endif return; } emptyNode[0] = EMPTY_NODE; emptyNode[1] = '\0'; for (int i = 1; i < size; i++) { char* data = dataList->get(i, dataList); if (strcmp(data, emptyNode) != 0) { struct BinaryNode* binaryNode = malloc(sizeof(struct BinaryNode)); if (binaryNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return; } binaryNode->data = data; binaryNode->left = NULL; binaryNode->right = NULL; queue->push(binaryNode, queue); struct BinaryNode* front = queue->front(queue); if (i % 2 == 0) { front->right = binaryNode; } else { front->left = binaryNode; } } else { queue->push(emptyNode, queue); } if (i % 2 == 0) { queue->pop(queue); } } free(emptyNode); emptyNode == NULL; queue->destroy(queue); } struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return; } binaryTree->root = rootNode; binaryTree->depth = log2(size + 1); return binaryTree; } void recursionPreorderTraversal(struct BinaryNode* binaryNode) { if (binaryNode == NULL) { return; } printf("%s ", binaryNode->data); recursionPreorderTraversal(binaryNode->left); recursionPreorderTraversal(binaryNode->right); } void recursionInorderTraversal(struct BinaryNode* binaryNode) { if (binaryNode == NULL) { return; } recursionInorderTraversal(binaryNode->left); printf("%s ", binaryNode->data); recursionInorderTraversal(binaryNode->right); } void recursionSubsequentTraversal(struct BinaryNode* binaryNode) { if (binaryNode == NULL) { return; } recursionSubsequentTraversal(binaryNode->left); recursionSubsequentTraversal(binaryNode->right); printf("%s ", binaryNode->data); } void foreachRecursionBinaryTree(struct BinaryTree* binaryTree, enum Sequence sequence) { if (binaryTree == NULL || binaryTree->root == NULL) { #if PRINT printf("二叉树不存在!\n"); #endif return; } struct BinaryNode* binaryNode = binaryTree->root; if (sequence == 1) { printf("先序:"); recursionPreorderTraversal(binaryNode); printf("\n"); } else if (sequence == 2) { printf("中序:"); recursionInorderTraversal(binaryNode); printf("\n"); } else if (sequence == 3) { printf("后序:"); recursionSubsequentTraversal(binaryNode); printf("\n"); } else { printf("未知遍历顺序!\n"); } } struct BinaryNodeStack { char* data; struct BinaryNodeStack* left; struct BinaryNodeStack* right; int flag; }; struct BinaryNodeStack* convertBinaryNode(struct BinaryNode* node) { if (node == NULL) { return NULL; } struct BinaryNode* left = convertBinaryNode(node->left); struct BinaryNode* right = convertBinaryNode(node->right); struct BinaryNodeStack* treeNode = malloc(sizeof(struct BinaryNodeStack)); if (treeNode == NULL) { return NULL; } treeNode->data = node->data; treeNode->left = left; treeNode->right = right; treeNode->flag = 0; return treeNode; } void preorderTraversal(struct BinaryNodeStack* binaryNode, struct SequenceStack* stack) { if (binaryNode->right != NULL) { stack->push(binaryNode->right, stack); } if (binaryNode->left != NULL) { stack->push(binaryNode->left, stack); } stack->push(binaryNode, stack); } void inorderTraversal(struct BinaryNodeStack* binaryNode, struct SequenceStack* stack) { if (binaryNode->right != NULL) { stack->push(binaryNode->right, stack); } stack->push(binaryNode, stack); if (binaryNode->left != NULL) { stack->push(binaryNode->left, stack); } } void subsequentTraversal(struct BinaryNodeStack* binaryNode, struct SequenceStack* stack) { stack->push(binaryNode, stack); if (binaryNode->right != NULL) { stack->push(binaryNode->right, stack); } if (binaryNode->left != NULL) { stack->push(binaryNode->left, stack); } } void recursionStackBinaryTree(struct BinaryTree* binaryTree, int sequence) { if (binaryTree == NULL || binaryTree->root == NULL) { #if PRINT printf("二叉树不存在!\n"); #endif return; } struct BinaryNodeStack* binaryNode = convertBinaryNode(binaryTree->root); struct SequenceStack* stack = initSequenceStack(); stack->push(binaryNode, stack); while (stack->size(stack) > 0) { struct BinaryNodeStack* currentNode = stack->top(stack); stack->pop(stack); if (currentNode->flag == 0) { currentNode->flag = 1; if (sequence == 1) { preorderTraversal(currentNode, stack); } else if (sequence == 2) { inorderTraversal(currentNode, stack); } else if (sequence == 3) { subsequentTraversal(currentNode, stack); } continue; } printf("%s ", currentNode->data); } stack->destroy(stack); } void foreachStackBinaryTree(struct BinaryTree* binaryTree, enum Sequence sequence) { if (sequence == 1) { printf("先序:"); recursionStackBinaryTree(binaryTree, sequence); printf("\n"); } else if (sequence == 2) { printf("中序:"); recursionStackBinaryTree(binaryTree, sequence); printf("\n"); } else if (sequence == 3) { printf("后序:"); recursionStackBinaryTree(binaryTree, sequence); printf("\n"); } else { printf("未知遍历顺序!\n"); } } void foreachBinaryTreeTier(struct BinaryTree* binaryTree) { if (binaryTree == NULL || binaryTree->root == NULL) { #if PRINT printf("二叉树不存在!\n"); #endif return; } struct BinaryNode* binaryNode = binaryTree->root; printf("层序遍历:"); struct SequenceQueue* queue = initSequenceQueue(); queue->push(binaryNode, queue); while (queue->isNotEmpty(queue)) { struct BinaryNode* currentNode = queue->front(queue); printf("%s ", currentNode->data); if (currentNode->left != NULL) queue->push(currentNode->left, queue); if (currentNode->right != NULL) queue->push(currentNode->right, queue); queue->pop(queue); } queue->destroy(queue); printf("\n"); } static struct SortArray { char* data; int index; }; void sortPreorderOrSubsequentArray(struct SortArray* array, int size) { int flag = 0; struct SortArray* temp = malloc(sizeof(struct SortArray)); for (int i = 0; i < size; i++) { for (int j = 0; j < size - 1 - i; j++) { if (array[j].index > array[j + 1].index) { flag = 0; temp->data = array[j].data; temp->index = array[j].index; array[j].data = array[j + 1].data; array[j].index = array[j + 1].index; array[j + 1].data = temp->data; array[j + 1].index = temp->index; } else { flag++; } } if (flag == (size - 1 - i)) { break; } flag = 0; } free(temp); temp = NULL; } // Use Preorder And Inorder create BinaryTree. struct ArrayList* convertPreorderOrSubsequent(struct ArrayList* preorderOrSubsequent, struct ArrayList* referTo) { int preorderOrSubsequentSize = preorderOrSubsequent->size(preorderOrSubsequent); int referToSize = referTo->size(referTo); struct SortArray* array = malloc(sizeof(struct SortArray) * referToSize); if (array == NULL) { #if PRINT printf("创建二叉树获取子树前序遍历顺序失败,内存空间不足!\n"); #endif return NULL; } for (int i = 0; i < referToSize; i++) { for (int j = 0; j < preorderOrSubsequentSize; j++) { if (strcmp(referTo->get(i, referTo), preorderOrSubsequent->get(j, preorderOrSubsequent)) == 0) { array[i].data = preorderOrSubsequent->get(j, preorderOrSubsequent); array[i].index = j; continue; } } } sortPreorderOrSubsequentArray(array, referToSize); struct ArrayList* resultList = initArrayList(5); for (int i = 0; i < referToSize; i++) { resultList->insert(i, array[i].data, resultList); } free(array); array = NULL; return resultList; } struct BinaryNode* createNodeBySequenceAndInorder(struct ArrayList* sequence, struct ArrayList* inorder, int foreOrHind, int* depth, int probe) { if (sequence->size(sequence) <= 0 || inorder->size(inorder) <= 0) { return NULL; } char* parentData = NULL; if (foreOrHind == 0) { parentData = sequence->get(0, sequence); } else { parentData = sequence->get(sequence->size(sequence) - 1, sequence); } struct BinaryNode* binaryNode = malloc(sizeof(struct BinaryNode)); if (binaryNode == NULL) { #if PRINT printf("创建二叉树结点失败,内存空间不足!\n"); #endif return; } binaryNode->data = parentData; binaryNode->left = NULL; binaryNode->right = NULL; if (sequence->size(sequence) == 1 || inorder->size(inorder) == 1) { if (*depth < probe) *depth = probe; return binaryNode; } // find parent node index by inorder. int size = inorder->size(inorder), index = 0; for (int i = 0; i < size; i++) { if (strcmp(inorder->get(i, inorder), parentData) == 0) { index = i; break; } } struct ArrayList* leftInorder = initArrayList(5); inorder->copy(inorder, leftInorder, 0, index); binaryNode->left = createNodeBySequenceAndInorder(convertPreorderOrSubsequent(sequence, leftInorder), leftInorder, foreOrHind, depth, ++probe); leftInorder->destroy(leftInorder); probe--; struct ArrayList* rightInorder = initArrayList(5); inorder->copy(inorder, rightInorder, index + 1, -1); binaryNode->right = createNodeBySequenceAndInorder(convertPreorderOrSubsequent(sequence, rightInorder), rightInorder, foreOrHind, depth, ++probe); rightInorder->destroy(rightInorder); return binaryNode; } struct BinaryTree* initTreeBySequenceAndInorder(char* sequenceArray, char* inorderArray, int foreOrHind) { struct ArrayList* sequence = createArrayByBinaryTree(sequenceArray); struct ArrayList* inorder = createArrayByBinaryTree(inorderArray); int* depth = malloc(sizeof(int)); if (depth == NULL) { #if PRINT printf("创建二叉树计算深度失败,内存空间不足!\n"); #endif return NULL; } *depth = 0; struct BinaryNode* binaryNode = createNodeBySequenceAndInorder(sequence, inorder, foreOrHind, depth, 1); sequence->destroy(sequence); inorder->destroy(inorder); struct BinaryTree* binaryTree = malloc(sizeof(struct BinaryTree)); if (binaryTree == NULL) { #if PRINT printf("创建二叉树失败,内存空间不足!\n"); #endif return NULL; } binaryTree->root = binaryNode; binaryTree->depth = *depth; if (*depth == 0) { #if PRINT printf("空二叉树!\n"); #endif } free(depth); depth = NULL; setBinaryTreeFunction(binaryTree); return binaryTree; } |
测试:
#define _CRT_SECURE_NO_WARNINGS //规避C4996告警 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "binaryTree.h" int main() { char* list = "(A (B(,D(E(J),F)),C(G,H(,I(K)))))"; struct BinaryTree* listBinaryTree = initBinaryTree(list, 1); listBinaryTree->draw(listBinaryTree); char* array = "A B C D E F G H I J K L M N O"; struct BinaryTree* arrayBinaryTree = initBinaryTree(array, 0); arrayBinaryTree->draw(arrayBinaryTree); char* queue = "A B C D E F G H I J K L M N O"; struct BinaryTree* queueBinaryTree = initBinaryTree(queue, 2); queueBinaryTree->draw(queueBinaryTree); char preorder[19] = "A B D E G H C F I"; char inorder[19] = "D B G E H A C I F"; struct BinaryTree* binaryTree = initTreeBySequenceAndInorder(preorder, inorder, 0); binaryTree->draw(binaryTree); char subsequent[19] = "D G H E B I F C A"; struct BinaryTree* tree = initTreeBySequenceAndInorder(subsequent, inorder, 1); tree->draw(tree); return 0; } |
运行结果:
A / \ B C / \ / \ * D G H / \ / \ / \ / \ * * E F * * * I / \ / \ / \ / \ / \ / \ / \ / \ * * * * J * * * * * * * * * K * / / / / / / / / / / / / / / / / \ A / \ B C / \ / \ D E F G / \ / \ / \ / \ H I J K L M N O / / / / / / / / \ A / \ B C / \ / \ D E F G / \ / \ / \ / \ H I J K L M N O / / / / / / / / \ A / \ B C / \ / \ D E * F / \ / \ / \ / \ * * G H * * I * / / / / / / / / \ A / \ B C / \ / \ D E * F / \ / \ / \ / \ * * G H * * I * / / / / / / / / \ |