线索二叉树的原理和实现代码
約二分木は、次の記事を表示することができます。
Cデータ構造とアルゴリズム - 連結の基礎 - 木02:バイナリツリートラバーサルの確立と異なります
特定プリアンブルシーケンスと手がかりとして、あなたは、次の資料を参照することができます:
Cデータ構造とアルゴリズム - 連結の基礎 - 木07:ねじ付きバイナリ(二)
0x01のバイナリツリーの原点に手がかり
一般バイナリツリーで、リーフノードまたは唯一の子ノードが空ポインタフィールドがたくさんあるでしょう、これらのスペースが作成されていますが、効果が得られ、利用されていないスペースの無駄。
ソリューション:
先行親ノードとセットにNULLポインタのポイントの左の子Lタグの左の子には前駆体または実際の存在を指していることを示すためにフラグ変数。
ヌルポインタ後継親ノードの右の子と設定RTAGのフラグ変数は、右の子は、後継者または実際に指していることを示します。
関連概念上の注意:
前駆体:特定のバイナリツリートラバーサル順トラバーサル、ノード上のノードのトラバースを以下。
後続:特定のバイナリツリートラバーサル順トラバーサル、次のノードのトラバースにノードを下記します。
クルー:前任者と後継者と呼ばれる手がかりへのポインタポイント。
スレッド・バイナリ:バイナリ追加手がかり。
手がかり:バイナリツリーはNULLポインタを加えた手がかりのコース上の手がかりと呼ばれます。
0×02。スレッドバイナリ構造
違いは、通常のバイナリツリーは、LタグとRTAGフラグ変数よりも中二分木構造の手がかりは、子供の左と右の子供たちのアイデンティティを示すために、ということです。
また、バイナリキュー・プロセス、ちょうど訪問した要素へのグローバルな事前のポインタの必要性をねじ込み。
typedef struct TreeNode
{
char data;
struct TreeNode* Left;
struct TreeNode* Right;
int Ltag;//用于线索化的标志变量 左为1表示前驱 右为1表示后继,0表示自身是树中元素
int Rtag;
}TreeNode,*BinTree;//定义了这个结构体为 TreeNode ,这个二叉树指针为 BinTree
BinTree pre;
0x03の。ビルドはバイナリスレッド
ビルドはバイナリスレッドと共通のバイナリツリーは基本的に同じである確立し、差異は、バイナリツリーノードに各追加の手がかりは、あなたが変数の初期化について署名する必要があるということです。
特定の同様の設立コードと通常のバイナリツリー、リファレンスの章を確立します。この訪問のポイントを
説明:ない紛争を横断する方法を確立する手がかりのバイナリツリーは、基本的にまだバイナリツリーで、確立された順序は同じです。
//二叉树的建立:递归实现,先序遍历的创建方法
//注意此处BT为指针的指针,因为没有返回值,所以要把二叉树的指针完全改变掉,需要指针的指针
//str为传入的字符串,该字符串用于创建二叉树的数据域
//n为当前创建节点数据域时使用到str的位数
//返回值为记录二叉树创建完毕后,使用了多少字符
int CreateTree(BinTree *BT,char *str,int n)
{
printf("创建二叉树第 %d 个节点 ", n);
char ch = str[n];//每次使用一个字符赋值数据域
printf("%c\n", ch);//输出该数据
n++;
if (ch != '\0')//字符串没完就一直创建
{
if (ch == '#')//如果遇到字符 # ,说明下方没有了,是叶节点
{
*BT = NULL;//代表二叉树指针为空
}
else
{
*BT = (BinTree)malloc(sizeof(TreeNode));//*BT代表二叉树指针的值,BinTree表示这是一个指针,大小为一个二叉树结构体
if (!*BT)
{
exit(-1);//若分配空间失败,异常退出
}
(*BT)->data = ch;
(*BT)->Ltag = 0;//注意一定要有这个初始化步骤
(*BT)->Rtag = 0;
n=CreateTree(&(*BT)->Left,str, n);//取指针的地址给此函数的第一个参数,因为此参数为二级指针
n=CreateTree(&(*BT)->Right, str, n);
}
}
return n;
}
0x04の。ためにねじ付きバイナリねじ
手がかりは、様々な順序で行うことができるが、手がかりとトラバーサル順序のシーケンスは、唯一の対応することができます。
//中序遍历线索化
void inThreading(BinTree BT)
{
if(BT)
{
inThreading(BT->Left);
if (!BT->Left)//若左孩子不存在,则左孩子线索化,指向前驱节点,即上一个访问过的节点
{
BT->Ltag = 1;
BT->Left = pre;
}
if (pre && !pre->Right)//当遍历到BT时,若它的前驱pre存在,且前驱右孩子不存在,那么前驱的右孩子指向BT,即BT为pre的后继
{
pre->Rtag = 1;
pre->Right = BT;
}
pre = BT;//当准备遍历下一个节点时,用pre保存当前的节点
inThreading(BT->Right);//继续中序遍历完成线索化
}
}
0x05の。オーダーバイナリツリートラバーサル手がかり
バイナリシーケンスはねじ方法に対応します。
//中序遍历线索二叉树,非递归实现
void InOrderThreading(BinTree BT)
{
BinTree p = BT;//让p指向根节点
while (p)
{
while (p->Ltag == 0)
{
p = p->Left;
}//首先遍历到中序遍历第一个开始的节点
printf("%c -> ", p->data);//打印其数据
while (p->Rtag == 1&&p->Right)//如果右孩子是线索,那么一直遍历至右孩子,因为右孩子指向后继
{
p = p->Right;
printf("%c -> ", p->data);
}//右孩子不是线索了,说明右孩子存在,左孩子刚遍历到了,所以接着右孩子的遍历,然后开始下一轮的遍历
p = p->Right;
}
}
0x06でプラスヘッドノードねじバイナリー
スクラッチトラバースから、だけでなく、尾のいずれかから、バイナリツリーが二重にリンクされたリストのようなものですので、こと、順トラバーサルで最後のノードに右の子ポイント、ルートノードにその左の子ポイント、バイナリツリーのルートの前にヘッドノードを増やしますトラバーサル。
手がかり:
//增加头结点的中序线索化,Head为二级指针,指向指针的指针,目的是真正改变一级指针的内容
void InOrderThreading(BinTree* Head, BinTree BT)
{
*Head =(BinTree)malloc(sizeof(TreeNode));//给头结点分配空间
(*Head)->Ltag = 0;//左孩子是根结点
(*Head)->Rtag = 1;//右孩子是线索
(*Head)->Right = (*Head);//右孩子先指向自身,防止原二叉树为空
if (!BT)//二叉树为空
{
(*Head)->Left = (*Head);//左孩子也指向自身
return;
}
pre = (*Head);//前指针指向头结点
(*Head)->Left = BT;
inThreading(BT);//开始普通的中序线索化二叉树
//此时二叉树中序序列的第一个结点的左孩子指向头结点
//结束普通的线索化后,此时的pre指针指向最后一个元素
pre->Right = *Head;
pre->Rtag = 1;
(*Head)->Right = pre;
//现在整个二叉树就是一个双向链表,既可以从头结点开始往后遍历,也可以从中序序列最后一个结点开始往前遍历
}
トラバーサル:
//有头结点的中序遍历线索二叉树,此时的BT是头结点
void InOrderThreading1(BinTree BT)
{
BinTree p = BT->Left;//让p指向根节点
while (p!=BT)//循环到尾部时,因为尾部右孩子指向头结点,所以,p等于头结点的时候遍历完成
{
while (p->Ltag == 0)
{
p = p->Left;
}//首先遍历到中序遍历第一个开始的节点
printf("%c -> ", p->data);//打印其数据
while (p->Rtag == 1 && p->Right!=BT)//此处右孩子的条件应为不等于BT
{
p = p->Right;
printf("%c -> ", p->data);
}//右孩子不是线索了,说明右孩子存在,左孩子刚遍历到了,所以接着右孩子的遍历,然后开始下一轮的遍历
p = p->Right;
}
}
主な機能ノードテストコードを導きます。
int main()
{
BinTree BT;//注意BT为指针
int k;//记录创建函数的返回值
char str[100];
printf("\n\n**********\t请输入一串字符用于创建二叉树:");
gets_s(str);
k = CreateTree(&BT, str, 0);
printf("\n\n**********\t二叉树创建成功!!!共使用了 %d 个字符",k);
BinTree Head;
printf("\n\n**********\t开始中序线索化该二叉树......");
InOrderThreading(&Head,BT);
printf("\n\n**********\t线索化成功!!!");
printf("\n\n**********\t开始该线索二叉树的中序遍历......");
printf("\n\n**********\t中序遍历结果为:");
InOrderThreading1(Head);
}
試験データ:AB#D ## C ##
0x07に。疑問や洞察
1.バイナリツリーヌル・ポインタが限定されるべきである、なぜ唯一のツリー全体でこれらのヌル・ポインタが後継者を見つけることができ、各ノードに到達するための手がかりを手がかりにできるようになりますか?
A:観測され、子どもたちは、ノード情報を印刷するには、左から戻る前に、子を返す権利がペースで終わった、行きがけ機能、このノードを印刷するには注意を払うが、ノードへの本当のトラバースで、それはあります子どもを返す方法を子供が空のままの場合は、関数呼び出しの全体のプロセスを完了した、部分的に完成し、関数呼び出し左ならば、ただ、前任者に子供を児童ポイントを左に、子供が空のままにされたときに、それを左に、または部分的に子供を残しました、リーフノードからのリターン、どのノードでも子が存在しているの周りに横断することを理解することができます。子供を残し、その後、子供の左と右の子供は全体のコールの完了後に返され、というように、常にリターンのルートを検索しますそれの特定の部分の自由ノードと次のノードが2つのノードが存在している子どもたちが後継者と前任者のポインタの存在を持つことになりますので、こと、子どもたちがすべてではないこと、の上だそう。また、あなたが見ることができます定量的な関係にクリック:バイナリツリーのノードN、2Nポインタフィールドの合計、N個のブランチのノードN-1の合計、それを2 N-ため (N-1)= N + 1つのポインタ、実際にヌルポインタフィールドを、実際にn-1個のポインタポイントは、ノードが2人の子供があっても(関係ツリーを完了するために、に応じて順番に横断することができます最初のノードが、前駆体を横断しない、請求順序を見下ろし)後継前駆体を見つけ、最後のノードは後継者を横断しません。だから、実際にはNULLポインタドメインは十分です。
どのような状況下2.バイナリスレッド?
A:多くの場合、ジャンクションやポイントを通過する必要性を見つけた場合、使用することができる、それははるかに少ない再帰法の時間計算量を超えています。
3.同じ原理については、後に、シーケンスの前に、実際には、順トラバーサルで手がかりを理解しています。