記事ディレクトリ
- 5.3.1 ツリーストレージ構造
-
- 5. 左息子と右兄弟のリンク構造
- 5.3.2 ノード取得アルゴリズム
-
- 1.長男ノードと長兄ノードを取得する
- 2. 指定されたノードの親を検索します
-
- a. アルゴリズム FindFather
- b. アルゴリズム分析
- c. コードの実装
- 3. コードの統合
5.3.1 ツリーストレージ構造
5. 左息子と右兄弟のリンク構造
[データ構造] ツリーと二分木 (19): ツリーの記憶構造 - 左息子、右兄弟リンク構造 (ツリー、フォレスト、二分木の変換)
左息子、右 兄弟リンク構造は、各ノードの 3 つのフィールド (FirstChild、Data、NextBrother) を使用してツリーを構築し、ツリーにバイナリ ツリーのプロパティを持たせます。具体的には、各ノードには次の情報が含まれます。
- FirstChild: このノードの長男 (左端の子ノード) へのポインタを格納します。このポインターを使用すると、ノードの最初の子ノードをすばやく見つけることができます。
- データ: にはノード データが保存されます。
- NextBrother: ノードの一番上の兄弟 (同じレイヤー内の右の兄弟ノード) へのポインタを格納します。このポインターを使用すると、同じレイヤー内のノードの次の兄弟ノードをすばやく見つけることができます。
この構造により、木全体を左子・右兄弟リンク構造を用いた二分木として表現することができます。この表現は、バイナリ ツリー、バイナリ ツリー フォレストなどの一部の特殊なツリー構造で使用されることがあります。この構造の利点の 1 つは、兄弟関係を表すための追加のポインターを必要とせずに、ツリーをよりコンパクトに表現できることです。
A
/|\
B C D
/ \
E F
A
|
B -- C -- D
|
E -- F
今すぐ:
A
/
B
\
C
/ \
E D
\
F
5.3.2 ノード取得アルゴリズム
1.長男ノードと長兄ノードを取得する
【データ構造】木と二分木(20):長男ノードと長兄ノードを求める木のアルゴリズム(GFC、GNB)
2. 指定されたノードの親を検索します
- 再帰的思考
- 与えられたノードは、与えられたノードが特定のノード (p など) へのポインタであることを意味します。
- 戻り値も、ノード p の親を指すポインターでなければなりません (見つからない場合は null)。
a. アルゴリズム FindFather
b. アルゴリズム分析
アルゴリズム FindFather は、ルート優先探索と同様に、t をルートポインタとしてツリー内でポインタ p が指すノードの親ノードを探索し、その時間計算量は O(n) です。
- まず、結果ポインタを null に設定します。
- t が空、p が空、または p が t に等しい場合は、直接戻ります。
- ポインタ q が t の最初の子ノードを指すようにします。
- q が空でない限りループに入ります。
- q が p に等しい場合、p の親ノードが見つかり、結果ポインタが t を指し、その後戻ることを意味します。
- それ以外の場合は、FindFather 関数を再帰的に呼び出し、パラメーター q と p を渡し、結果を result に格納します。
- 結果が空でない場合は、親ノードが見つかったことを意味し、直接返されます。
- ポインタ q を q の次の兄弟ノードに更新します。
- ループの最後でも親ノードが見つからない場合、結果は空のままです。
c. コードの実装
void FindFather(TreeNode* t, TreeNode* p, TreeNode** result) {
*result = NULL;
if (t == NULL || p == NULL || p == t) {
return;
}
TreeNode* q = t->firstChild;
while (q != NULL) {
if (q == p) {
*result = t;
return;
}
FindFather(q, p, result);
if (*result != NULL) {
return;
}
q = q->nextBrother;
}
}
再帰的処理の概要:
- ツリーが空であるか、指定されたノードが空であるか、または指定されたノードがルート ノードである場合、定義により NULL が返されます。
q
ルートノードの左側の子ノードをポイントし、ループに入ります。- 指定されたノードがルート ノードの左の子である場合、ルート ノードはその親になります。
- 左側のサブツリー内再帰検索
- …………
- 親ノードが見つかった場合は、直接戻ります
- が見つからない場合、
q
は左の息子の右の兄弟 (つまり、次の子ノード) に更新されます。- 再帰的検索を続行します...
3. コードの統合
#include <stdio.h>
#include <stdlib.h>
// 定义树节点
typedef struct TreeNode {
char data;
struct TreeNode* firstChild;
struct TreeNode* nextBrother;
} TreeNode;
// 创建树节点
TreeNode* createNode(char data) {
TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));
if (newNode != NULL) {
newNode->data = data;
newNode->firstChild = NULL;
newNode->nextBrother = NULL;
}
return newNode;
}
// 释放树节点及其子树
void freeTree(TreeNode* root) {
if (root != NULL) {
freeTree(root->firstChild);
freeTree(root->nextBrother);
free(root);
}
}
// 算法 FindFather
void FindFather(TreeNode* t, TreeNode* p, TreeNode** result) {
*result = NULL;
if (t == NULL || p == NULL || p == t) {
return;
}
TreeNode* q = t->firstChild;
while (q != NULL) {
if (q == p) {
*result = t;
return;
}
FindFather(q, p, result);
if (*result != NULL) {
return;
}
q = q->nextBrother;
}
}
int main() {
// 构建左儿子右兄弟链接结构的树
TreeNode* A = createNode('A');
TreeNode* B = createNode('B');
TreeNode* C = createNode('C');
TreeNode* D = createNode('D');
TreeNode* E = createNode('E');
TreeNode* F = createNode('F');
A->firstChild = B;
B->nextBrother = C;
C->nextBrother = D;
C->firstChild = E;
E->nextBrother = F;
// // 层次遍历算法
// printf("Level Order: \n");
// LevelOrder(A);
// printf("\n");
// 要查找父亲的节点
TreeNode* targetNode = E;
TreeNode* result = NULL;
// 使用算法 FindFather 查找父亲
FindFather(A, targetNode, &result);
// 输出结果
if (result != NULL) {
printf("The father of %c is %c\n", targetNode->data, result->data);
} else {
printf("Node not found or it is the root.\n");
}
// 释放树节点
freeTree(A);
return 0;
}