ツリーは、n個(n> = 0)個のノードの有限集合です。
空でないツリーの場合:
(1)ルートと呼ばれる特定のノードは1つだけです。
(2)n> 1の場合、残りのノードはm(m> 0)の互いに素な有限集合に分割できます。各集合はそれ自体がツリーであり、ルートノードのサブツリー(SubTree)と呼ばれます。
示されているように:
ツリーの概念
ノード(次数):要素と、そのサブツリーを指すいくつかのノードが含まれます。
ノード次数:ノードが所有するサブツリーの数。
リーフまたはターミナルノード:次数0のノード。
非終端ノードまたは分岐ノード:次数が0ではないノード。
ツリーの次数:ツリー内の各ノードの次数の最大値。
子:ノードの子ツリーのルートは、ノードの子と呼ばれます。
親:子の親ノード。
兄弟:同じ親の子供は兄弟と呼ばれます。
いとこ:親が同じレベルにあるノードはいとこです。
祖先:ルートからノードのブランチまでのすべてのノード。
子孫:特定のノードをルートとするサブツリー内のノードは、そのノードの子孫と呼ばれます。
ノードレベル(レベル):ルートの定義から始めて、ルートは最初のレベルです。
ツリーの深さ(深さ)または高さ:ツリーの最大接合深さは、ツリー階層ポイントと呼ばれます。
森(森):m(m> = 0)互いに素な木のコレクション。
ツリーストレージ構造
親の表記
各ノードには、リンクリスト内の親ノードの位置を示すインジケーターが付加されています。
データ | 親 |
データはデータフィールドであり、親はポインタフィールドであり、ノードの親の添え字を配列に格納します。
/*树的双亲表示法结点结构定义*/
# define MAX_TREE_SIZE 100
type int ElemType;
typedef struct PTNode
{
TElemType data;/*结点数据*/
int parent;/*双亲位置*/
}PTNode;
typedef struct
{
PTNode nodes[MAX_TREE_SIZE];/*结点数组*/
int r,n;/*根的位置和结点数*/
}PTree;
ポインタドメインは、親ドメイン、長子ドメイン、左兄弟ドメインなど、実際の要件に応じて構造を拡張できます。
子表記
各ノードには複数のポインタフィールドがあり、各ポインタはサブツリーのルートノードを指します。この表現方法は、複数のリンクリスト表現と呼ばれます。
1.ポインタフィールドの数はツリーの次数と同じです
この方法は、ツリー内のノードの次数のわずかな違いに適しており、各ノードのスペースが十分に活用されていません。
2.各ノードのポインタフィールドの数はノードの次数に等しく、ノードフィールドの数を格納するための場所が取得されます
各ノードの次数差が比較的大きい場合に適用できます。
各ノードのサブツリーは構造が異なるため、維持するノードの次数の値を追加すると、時間のロスが発生します。
改善する
各ノードの子を配置し、ストレージ構造として単一リンクリストを使用します。n個のノードにn個の子リンクリストがあります。リーフノードの場合、単一リンクリストは空です。n個のヘッドポインタは、シーケンシャルストレージ構造を持つ線形テーブルを形成します。
/*树的孩子表示法结构定义*/
/*
需要设计两种结点结构
一种是孩子链表的孩子结点 |child|next|【第一个域表示该结点所指向的结点,next表示该结点同一层级的相邻结点】
另一种是表头数组的表头结点 |data|firstchild|【第一个域表示结点的数据,第二个结点表示与该结点子树的 第一个孩子的下标】
*/
# define MAX_TREE_SIZE 100
/*孩子结点*/
typedef struct CTNode
{
int child;
struct CTNode *next;
}*ChildPtr;
/*表头结构*/
typedef struct
{
TElemType data;
ChildPtr firstchild;
};
/*树结构*/
{
CTBox[MAX_TREE_SIZE];/*结点数组*/
int r,n;/*根位置和结点树*/
}
親子表記
もう1つの場所を使用して、ノードの親を表します。
子兄弟表記
最初の子が存在する場合、どのツリーも一意であり、存在する場合、その右の兄弟は一意です。
したがって、ノードの最初の子とノードの右の兄弟を指すように2つのポインターを設定できます。
データ | 第一子 | rightsib |
/*树的孩子兄弟表示法结构*/
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;
親ポインタフィールドを追加して、必要に応じて親をすばやく見つけることもできます。
二分木
二分木の特徴は、各ノードに最大2つのサブツリーがあり(つまり、二分木に2より大きい次数のノードがない)、二分木が左右に分割され、順序ができないことです。逆になります。
ADT
ADT BinaryTree{
数据对象D:D是具有相同特性的数据元素的集合
数据关系R:
若D = Φ,则R = Φ,称BinaryTree为空二叉树
若D ≠ Φ,则R = {H},H有如下二元关系:
(1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2)若D-{root} ≠ Φ,则存在D - {root} = {Dl,Dr},且Dl ∩ Dr = Φ;
(3)若Dl ≠ Φ,则Dl中存在唯一的元素xl,<root,xl>∈H,且存在Dl上的关系Hl⊂H;
若Dr ≠ Φ,则Dr中存在唯一的元素xr,<root,xr>∈H,且存在Dr上的关系Hr⊂H;
H = {<root,xl>,<root,xr>,Hl,Hr};
(4)(Dl,{Hl})是一棵符合本定义的二叉树,称为根的左子树,
(Dr,{Hr})是一棵符合本定义的二叉树,称为根的右子树;
基本操作P:
InitBiTree(&T);
操作结果:构造空的二叉树T。
DestoryBiTree(&T);
初始条件:二叉树T存在。
操作结构:销毁二叉树T。
CreateBiTree(&T,definition);
初始条件:definition给出二叉树T的定义。
操作结果:按definition构造二叉树T。
ClearBiTree(&T);
初始条件:二叉树T存在。
操作结果:将二叉树清为空树。
BiTreeEmpty(T);
初始条件:二叉树T存在。
操作结果:若T为空二叉树,返回TRUE,否则返回FALSE。
BiTreeDepth(T);
初始条件:二叉树T存在。
操作结果:返回T的深度。
Root(T);
初始条件:二叉树T的根存在。
操作结果:返回T的根。
Value(T,e);
初始条件:二叉树T存在,e是T中某个结点。
操作结果:返回e的值。
Assign(T,&e,value);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:结点e赋值为value。
Parent(T,e);
初始条件:二叉树T存在,e是T中的结点。
操作结果:若e是T的非根结点,则返回它的双亲,否则返回"空"。
LeftChild(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的左孩子。若e无左孩子,则返回"空"。
RightChild(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的右孩子。若e无右孩子,则返回"空"。
LeftSibling(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的左兄弟。若e是T的左孩子或无左兄弟,则返回"空"。
RightSibling(T,e);
初始条件:二叉树T存在,e是T中的某个结点。
操作结果:返回e的右兄弟。若e是T的右孩子或无右兄弟,则返回"空"。
InsertChild(T,p,LR,c);
初始条件:二叉树T存在,p指向T中的某个结点,LR为0或1,非空二叉树c与T不想交且右子树为空。
操作结果:根据LR为0或1,插入c为T中p所指结点的左或右子树。p所指结点的原有左或右子树则成为c的右子树。
DeleteChile(T,p,LR);
初始条件:二叉树T存在,p指向T中的某个结点,LR为0或1.
操作结果:根据LR为0或1,删除T中p所指结点的左或右子树。
PreOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:先序遍历T,对每个结点调用函数visit一次且仅一次。一旦visit失败,则操作失败。
InOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:中序遍历T,对每个结点调用函数visit一次且仅一次。一旦visit失败,则操作失败。
PostOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:后序遍历T,对每个结点调用函数visit一次且仅一次。一旦visit失败,则操作失败。
LevelOrderTraverse(T,visit());
初始条件:二叉树T存在,visit()是对结点操作的应用函数。
操作结果:层序遍历T,对每个结点调用函数visit一次仅且一次。一旦visit失败,则操作失败。
}ADT BinaryTree
二分木の5つの基本的な形式
1.空の木
2.ルートノードは1つだけです
3.ルートノードには左側のサブツリーのみがあります
4.ルートノードには適切なサブツリーのみがあります
5.ルートノードには、左側のサブツリーと右側のサブツリーの両方があります
特別な二分木
1.斜めの木
右傾斜木:すべてのノードが左のサブツリーのみを持つ二分木
左斜め木:すべてのノードが右サブツリーのみを持つ二分木
2.完全な二分木
すべてのブランチノードには左のサブツリーとサブツリーがあり、葉はすべて同じレイヤーにあります。
特徴:
(1)葉は最下層にしか現れず、他の層に現れるとバランスが取れません。
(2)非リーフノードの次数は2でなければなりません。
(3)同じ深さの二分木では、完全な二分木が最も多くのノードと最も多くの葉を持っています。
3.完全な二分木
n個のノードを持つ二分木はレベルのシーケンスで番号が付けられます。同じ深さの完全な二分木でi(1 <= i <= n)の番号が付けられたノードとiの番号が付けられたノードが、バイナリ内でまったく同じ位置にある場合ツリー、二分木は完全な二分木と呼ばれます。
特徴
(1)リーフノードは下の2つのレイヤーにのみ表示されます
(2)一番下の葉は左側の連続した位置に集中する必要があります
(3)最後から2番目の層、リーフノードがある場合、それらはすべて右側で連続している必要があります
(4)ノードの次数が1の場合、ノードには左の子のみがあり、右のサブツリーはありません。
(5)ノード数が同じ二分木の場合、完全な二分木の深さが最小になります[中立位置なし]。
二分木の性質
プロパティ1:i番目のレイヤーには最大で1つのノードがあります(i> = 1)
プロパティ2:深さkの二分木は最大で-1ノード(i> = 1)を持ちます
プロパティ3:任意の二分木Tの場合、そのリーフノードはn0であり、次数2のノードの数はn2であるため、n0 = n2 + 1
どのツリーでも、リーフノードの数n0 =(k-1)+(k-2)+(k-3)+ ... + 1
プロパティ4:n個のノードを持つ完全な二分木の深さは 、[切り捨て]です。
プロパティ5:n個のノード(深さは)を持つ完全な二分木がある場合、ノードには順序に従って番号が付けられ、任意のノードiに対して(1 <= n <= n)があります。
1. i = 1の場合、ノードiは親のない二分木のかかとです。i> 1の場合、その親はノード⌊i/2⌋です。
2. 2i> nの場合、ノードiには左の子がありません(ノードiはリーフノードです)。それ以外の場合、その左の子はノード2iです。
3. 2i + 1> nの場合、ノードiの右の子、それ以外の場合、右の子はノード2i +1です。
二分木のストレージ構造
二分木のシーケンシャルストレージ構造
完全な二分木はこのストレージ構造に適しており、ノードの番号は配列の対応する位置に対応します。
二分木のチェーンストレージ構造
バイナリリンクリスト:1つのデータフィールド、2つのポインタフィールド
親ポインタドメインは、特定のニーズに応じて追加できます。これは、3極リンクリストと呼ばれます。
/*二叉树的二叉链表结点结构*/
typedef struct BiNode
{
TElemeType data;
struct BiNode *lchild,*rchild;
}BiNode,*BTree;
二分木をトラバースする
バイナリツリートラバース:ルートノードから開始し、特定の順序に従って、各ノードが1回アクセスされ、1回だけアクセスされるように、バイナリツリー内のすべてのノードに順番にアクセスします。【左から右に限定】
プレオーダー(ルート)トラバーサル
二分木が空の場合、操作はありません。それ以外の場合:
(1)ルートノードにアクセスします。
(2)最初に左側のサブツリーをトラバースします。
(3)最初に右側のサブツリーをトラバースします。
Status PreOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
if(T)
{
if(visit(T->data))
if(PreOrderTraverse(T->lchild,visit))
if(PreOrderTraverse(T->rchild,visit))
return OK;
return ERROR;
}
else
return OK;
}
ミドルオーダー(ルート)トラバーサル
二分木が空の場合、操作はありません。それ以外の場合:
(1)左側のサブツリーの順序どおりの走査。
(2)ルートノードにアクセスします。
(3)右側のサブツリーの順序どおりの走査。
/*递归遍历二叉树*/
Status InOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
if(T)
{
if(InOrderTraverse(T->lchild,visit))
if(visit(T->data))
if(InOrderTraverse(T->rchild,visit))
return OK;
return ERROR;
}
else
return OK;
}
スタックの最上位レコードのポインターが空でない場合は、左側のサブツリーをトラバースする必要があります。つまり、左側のサブツリーへのポインターがスタックにプッシュされます。
スタックの最上位レコードのポインタが空の場合は、上位レベルに戻る必要があります。左側のサブツリーから戻る場合は、現在のレベル、つまり最上位のポインタが指すルートノードにアクセスする必要があります。スタックの記録。
右側のサブツリーから戻った場合は、現在のレイヤーのトラバーサルが終了したことを示しており、スタックは引き続き終了する必要があります。[ルートノードを保存する必要はありません。スタックの最上位レコードを変更するだけです]
/*中序遍历的非递归实现*/
Status InOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
InitStack(S);
Push(S,T);//根指针进栈
while(!StackElempty(S))
{
while(GetTop(S,p))
push(S,p->lchild);//向左走到头
Pop(S,p);//空指针退栈
if(!StackEmpty(S))//访问结点,向右走一步
{
Pop(S,p);
if(!visit(p->data))
{
return ERROR;
}
Push(S,p->rchild);
}
}
}
Status InOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
InitStack(S);
p = T;
while(p || !StackEmpty(S))
{
if(p)
{//根指针进栈,遍历左子树
Push(S,p);
p = p->lchild;
}
else
{//根指针退栈,访问根结点,遍历右子树
Pop(S,p);
if(!visit(p->data))
return ERROR;
p = p->rchild;
}
}
}
注文後(ルート)トラバーサル
二分木が空の場合、操作はありません。それ以外の場合:
(1)左のサブツリーをポストオーダーでトラバースします。
(2)右のサブツリーをポストオーダーでトラバースします。
(3)ルートノードをトラバースします。
Status PostOrderTraverse(BiTree T,Status (*visit)(TElemType e))
{
if(T)
{
if(PostOrderTraverse(T->lchild,visit))
if(PostOrderTraverse(T->rchild,visit))
if(visit(T->data))
return OK;
return ERROR;
}
else
return OK;
}
トラバーサル結果を導き出す
プレオーダーとミドルオーダー-ポストオーダー
ミドルオーダーとポストオーダー-プレオーダー
事前注文と事後注文では二分木を決定できないため、順序どおりの順序を決定できません
二分木の樹立
二分木を展開します。
二分木の確立を容易にするために、仮想ノードは二分木の各ノードのヌルポインタから導出される必要があり、その値は特定の値です。
/*按前序输入二叉树的结点的值(字符)*/
/* .表示空树,构建二叉链表表示二叉树*/
Status CreateBiTree(BiTree &T)
{
scanf(&ch);
if(ch == ".")
T = NULL;
else
{
if(!(T = (BiTNode*)malloc(sizeof(BiTnode))))
exit(OVERFLOW);
T->data = ch;//生成根结点
CreateBiTree(T->lchild);//构建左子树
createBiTree(T->rchild);//构建右子树
}
return OK;
}
二分木の一般的なオペコードの実装
// 二叉树操作.cpp : 定义控制台应用程序的入口点。
//由于在vs中编写的代码,若scanf_s处出错可以改为scanf
#include "stdafx.h"
# include<stdio.h>
# include<algorithm>
# include<stdlib.h>
# include<stdexcept>
# include<queue>
# include<stack>
# include<iostream>
using namespace std;
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode *lChild;
struct BiTNode *rChild;
}BiTNode;//二叉树结点
typedef BiTNode *BiTree;//指向二叉树结点的指针
void InitBiTree(BiTree &T) {
T = NULL;
}//初始化二叉树
void ClearBiTree(BiTree &T) {
if (T) {
if (T->lChild)
ClearBiTree(T->lChild);
if (T->rChild)
ClearBiTree(T->rChild);
free(T);
T = NULL;
}
}//清空二叉树
bool BiTreeEmpty(BiTree T) {
return T == NULL ? true : false;
}//判断二叉树是否为空
bool CreateBiTree(BiTree &T) {
char ch;
scanf_s("%c",&ch);
if (ch == '.')
T = NULL;
else {
try {
if (!(T = (BiTNode *)malloc(sizeof(BiTNode))))
throw "内存分配不成功!";
}
catch(const char* msg){
printf("%s",msg);
return false;
}
T->data = ch;
CreateBiTree(T->lChild);
CreateBiTree(T->rChild);
}
return true;
}//先序创建二叉树
/*非递归创建二叉树*/
// 二叉树的建立(非递归)(按照先序遍历的顺序来生成一颗二叉树)
BiTree createBiTree()
{
char ch[20];
cin >> ch;
int len = (int)strlen(ch);
stack<BiTree> s;// 用来存储节点地址的栈
int flag = 1; // 标志位
int i = 0;
if (ch[i] == '.')
return NULL;
BiTree temp;
BiTree root = new BiTNode();
root->data = ch[i++]; // 此处应该加一个判断首字符是否为‘#’的判断,保证根节点不为空。
root->lChild = NULL;
root->rChild = NULL;
s.push(root);// 根节点入栈
while (i < len)
{
BiTree pNew = NULL;
if (flag == 1) // 创建左孩子
{
if (ch[i] == '.')
flag = 2;
else
{
pNew = new BiTNode();
pNew->data = ch[i];
pNew->lChild = NULL;
pNew->rChild = NULL;
temp = s.top(); // 栈顶元素(取出栈顶元素赋予temp,注意,这不是出栈操作,因为栈顶指针top没有变)
temp->lChild = pNew;
s.push(pNew); // 栈顶元素的左子树入栈
flag = 1;
}
}
else if (flag == 2) // 创建右孩子
{
if (ch[i] == '.')
flag = 3;
else
{
pNew = new BiTNode();
pNew->data = ch[i];
pNew->lChild = NULL;
pNew->rChild = NULL;
temp = s.top(); // 栈顶元素
temp->rChild = pNew;
s.push(pNew); // 栈顶元素的右子树入栈
flag = 1;
}
}
else // 左右孩子都已经创建完毕
{
temp = s.top();
s.pop(); // 栈顶元素出栈,并修改栈顶指针
while (!s.empty() && s.top()->rChild == temp) // 若此时栈中的元素个数仍大于1个,并且刚刚出栈的元素是当前栈顶元素的右子树,则继续让栈顶元素出栈,并修改栈顶指针。
s.pop();
flag = 2; // 跳出此while循环时,创建当前栈顶节点的右孩子,此时的i在ch[]中对应的数据刚好就是下一个待使用的数据,所以执行 --i是为了防止下面的 ++i跳过了该数据(即当前i对应的数据就是我们下一步创建右子树所应该使用的数据)。
--i;
}
++i;
}
return root;
}
int BiTreeDepth(BiTree T) {
int LD, RD;
if (T == NULL)
return 0;
else {
LD = BiTreeDepth(T->lChild);
RD = BiTreeDepth(T->rChild);
return max(LD, RD) + 1;
}
}//求树深度
TElemType Value(BiTree p) {
return p->data;
}//返回结点的值
void Assign(BiTree p, TElemType value) {
p->data = value;
}//为二叉树某结点赋值
BiTree Location(BiTree T, TElemType e) {
BiTree p = NULL;
if (T) {
if (T->data == e)
return T;
else
{
if (p = Location(T->lChild, e))
return p;
if (p = Location(T->rChild, e))
return p;
}
}
return p;
}
bool InsertBiTree(BiTree T, TElemType e, BiTree T0, int LR) {
BiTree p = Location(T, e);
if (p) {
T0->rChild = LR ? p->lChild : p->rChild;
LR ? p->lChild = T0 : p->rChild = T0;
return true;
}
return false;
}//将T0插入树T,成为结点e的子树,LR为左右子树标志,T0只有左子树
bool DeleteBiTree(BiTree T, TElemType e, int LR) {
BiTree p = Location(T, e);
if (p) {
LR ? ClearBiTree(p->lChild) : ClearBiTree(p->rChild);
return true;
}
return false;
}//删除e结点的左子树右子树,LR为删除标志
void Visit(TElemType e) {
printf("%c",e);
}
void PreOrderTraverse(BiTree T, void(Visit)(TElemType)) {
if (T) {
Visit(T->data);
PreOrderTraverse(T->lChild, Visit);
PreOrderTraverse(T->rChild, Visit);
}
}//先序遍历
void InOrderTraverse(BiTree T, void(Visit)(TElemType)) {
if (T) {
PreOrderTraverse(T->lChild, Visit);
Visit(T->data);
PreOrderTraverse(T->rChild, Visit);
}
}//中序遍历
void PostOrderTraverse(BiTree T, void(Visit)(TElemType)) {
if (T) {
PreOrderTraverse(T->lChild, Visit);
PreOrderTraverse(T->rChild, Visit);
Visit(T->data);
}
}//后序遍历
/*非递归遍历树*/
void F_PostOrderTraverse(BiTree &T) {
stack<BiTree> s;
BiTree cur, pre = NULL;
s.push(T);
while (!s.empty()) {
cur = s.top();
if ((cur->lChild == NULL && cur->rChild == NULL) || (pre !=NULL && (pre == cur->lChild || pre == cur->rChild))) {//如果当前节点没有孩子结点或左右结点均已访问
cout << cur->data;
s.pop();
pre = cur;
}
else
{
if (cur->rChild != NULL)
s.push(cur->rChild);
if (cur->lChild != NULL)
s.push(cur->lChild);
}
}
}
void LevelOrderTraverse(BiTree T, void(Visit)(TElemType)) {
BiTree p[100];//数指针数组
int i, j;
i = j = 0;
if (T)
p[j++] = T;
while (i < j) {
Visit(p[i]->data);
if (p[i]->lChild)
LevelOrderTraverse(p[i]->lChild, Visit);
if (p[i]->rChild)
LevelOrderTraverse(p[i]->rChild, Visit);
i++;
}
}//层序遍历
void PrintBiTree(BiTree &T, void(Visit)(TElemType)) {
int row, col;
int i, j, m, l, r;
BiTNode a[101][101] = {};
if (T)
{
row = BiTreeDepth(T);
col = pow(2, row) - 1;
for (i = 1; i <= row - 1; ++i)
{
for (j = 1; j <= pow(2, i - 1); j++)
{//只要二叉树的深度确定,其结点在二叉数组中的横纵坐标都可以确定
m = (2 * j - 1)*pow(2, row - i);//根结点在二叉数组中的列位置
l = (4 * j - 3)*pow(2, row - i - 1);//左孩子在二叉数组中的列位置
r = (4 * j - 1)*pow(2, row - i - 1);//有孩子在二叉数组中的列位置
if (i == 1)
a[i][m] = *T;
if (a[i][m].lChild)
a[i + 1][l] = *(a[i][m].lChild);
if (a[i][m].rChild)
a[i + 1][r] = *(a[i][m].rChild);
}
}
for (i = 1; i <= row; ++i)
{
for (j = 1; j <= col; ++j)
{
if (a[i][j].data)
printf("%c", a[i][j].data);
else
printf(" ");
}
printf("\n");
}
}
else
{
printf("THIS IS A EMPTY BINARY TREE\n");
}
}
int main()
{
BiTree T = createBiTree();
/* ABC..DE.G..F... */
//CreateBiTree(T);
printf("%d", BiTreeDepth(T));
PreOrderTraverse(T,Visit);
printf("\n");
PrintBiTree(T, Visit);
cout << endl;
F_PostOrderTraverse(T);
/*
BiTree p = NULL;
p = (BiTree)malloc(sizeof(BiTNode));
p->data = 'P';
p->lChild = p->rChild = NULL;
InsertBiTree(T, 'B', p, 1);
DeleteBiTree(T, 'B', 1);
printf("\n");
PrintBiTree(T, Visit);
*/
return 0;
}
手がかり二分木
nノードの二分木には2n個のポインタフィールドがあり、n-1個のポインタフィールドが使用されます。残りのn + 1個のポインタフィールドは無駄であり、手がかりを格納するために使用できます。
前任者と後継者へのポインタであると呼ばれる手がかりは、手がかりとバイナリリンクリストが呼び出された手がかりリンクリスト、および対応するバイナリツリーが呼び出されたスレッドバイナリツリー(スレッドバイナリツリー)
二分木を特定の順序でトラバースして手がかりの二分木に変えるプロセスは、手がかりと呼ばれます。
先行ノードと後続ノードを指すノードの総数は11であり、子へのポインタフィールドが追加され、スペースを最大限に活用して合計20になります。
ただし、lchildドメインが前の子を指しているのか、左の子を指しているのかを明確に判断するには、2つのフラグドメインが必要です[ブール型]
LTagが0の場合はノードの左の子を指し、LTagが1の場合はノードの先行子を指します。
RTagが0の場合はノードの右の子を指し、RTagが1の場合はノードの後続を指します。
/*二叉树的二叉线索存储结构的定义*/
typedef enum{Link,Thread} PointerTag;/*Link表示指向左右孩子的指针,
Thread表示指向前驱或后继的线索*/
typedef struct BiThrNode
{
TElemType data;/*结点数据*/
struct BiThrNode *lchild,*rchild;/*左右孩子指针*/
PointerTag LTag;
PointerTag RTag;/*左右标志*/
}BiThrNode,*BiThrTree;
手がかりの本質は、バイナリリンクリストのnullポインタを先行または後続の手がかりに変更するプロセスです。
手がかりのプロセスは、トラバーサルプロセス中にnullポインタを変更するプロセスです。
BiThrTree pre;/*全局变量,始终指向刚刚访问过的结点*/
Status InThreading(BiThrTreee p)
{
if(p)
{
InThreading(p->lchild);//递归左子树线索化
if(!p->lchild)
{//没有左孩子
p->LTag = Thread;//前驱线索
p->lchild = pre;
}
if(!pre->rchild)
{//前驱没有右孩子
pre->RTag = Thread;//后继线索
pre->rchild = p;//前驱右孩子指针指向后继
}
pre = p;//保持pre指向p(下一个)的前驱
InThread(p->rchild);//递归右子树线索化
}
}
二分木が空でない場合は、二分リンクリストにヘッドノードを追加し、そのlchildフィールドのポインターが二分木のルートノードを指し、そのrchildフィールドのポインターがの最後のノードを指すようにします。順序どおりのトラバーサル。
二分木が空の場合は、そのlchildフィールドとrchildフィールドのポインターがヘッドノードを指すようにします。
そうすることで、後続ノードに沿った最初のノードから、または先行ノードに沿った最後のノードからトラバースできます。