树型结构属于非线性结构(元素的前驱和后继的个数不是为1的),这一节讲的树形结构元素的前驱个数为1,但是元素的后继个数不是为1了(可以有多个后继),所以说树形机构元素的关系是一对多或者多对多的。树型结构的特点是节点之间是有分支的,并且还具有层次关系。
(1)倒悬树:最常用的表示形式
(2)嵌套集合:是一些集合的集体,对于任何两个集合,或者不想交,或者一个集合包含另一个集合
(3)广义表形式:例如(A(B(E(K,L),F),C(G(M,N),D(H,I,J)
(4)凹入法表示形式
度为m的树 | m叉树 |
---|---|
任意结点的度≤m(最多m个孩子) | 任意结点的度≤m(最多m个孩子) |
至少有一个结点度=m | 允许所有结点的度都 |
一定是非空树,至少有一个m+1个结点 | 可以是空树 |
3. 度为m的树第i层至多有mi-1 个结点(i≥1)
4. m叉树第i层至多有mi-1个结点(i≥1)
4. 高度为h的m叉树至多有 (mh-1) /(m-1)个结点:m0+m1…+mn
5. 高度为h的m叉树至少有h个结点
6. 高度为h,度为m的树至少有h+m-1个结点
7. 具有h个结点的m叉树的最小高度为[logm(n(m-1)+1)]
(1)一棵高度为h,且含有2h - 1个结点的二叉树
(2)特点:
①只有最后一层有叶子结点
②不存在度为 1 的结点
③按层序从 1 开始编号,结点 i 的左孩子为 2i,右孩子为 2i+1;结点 i 的父节点为 i/2 (如果有的话)
(3)基本特点:a.是每一层上的结点数总是最大结点数。b. 满二叉树的所有的支结点都有左、右子树。c. 可对满二叉树的结点进行连续编号,若规定从根结点开始,按“自上而下、自左至右”的原则进行。
(1)当且仅当其每个结点都与高度为h的满二叉树中编号为1~n的结点一一对应时,称为完全二叉树
(2) 特点:
①只有最后两层可能有叶子结点
②最多只有一个度为1的结点
③同左③ ④ i≤ n/2 为分支结点, i> n/2 为叶子结点
(3)完全二叉树是满二叉树的一部分,而满二叉树是完全二叉树的特例
(4)特点:① 若完全二叉树的深度为 k k k ,则所有的叶子结点都出现在第 K K K 层或 K − 1 K-1 K−1 层 ② 对于任意结点,如果其右子树的最大层次为 l l l ,则左子树的最大层次为 l l l 或 l + 1 l+1 l+1
(5)性质:
① n个结点的完全二叉树深度为 [ l o g 2 n ] + 1 [log~2~n]+1 [log 2 n]+1
②若对一棵有n个结点的完全二叉树(深度为 [ l o g 2 n ] + 1 [log~2~n]+1 [log 2 n]+1 的结点按层(从第1层到第 [ l o g 2 n ] + 1 [log~2~n]+1 [log 2 n]+1 层)序自左至右进行编号,则对于编号为 i i i ( 1 ≤ i ≤ n 1≤i≤n 1≤i≤n)的结点:
a. 若i=1:则结点i是二叉树的根,无双亲结点;否则,若i>1,则其双亲结点编号是[i/2]。
b. 如果2i>n:则结点i为叶子结点,无左孩子;否则,其左孩子结点编号是2i。
c. 如果2i+1>n:则结点i无右孩子;否则,其右孩子结点编号是2i+1。
1.设非空二叉树中度为0、1和2的结点个数分别为n0、n1和n2,则 n0 = n2 + 1(叶子结点比二分支结点多一个)
假设树中结点总数为 n,则
① n = n0 + n1 + n2 ② n = n1 + 2n2 +1 => ② - ①n0 = n2 + 1
2. 二叉树第 i 层至多有 2i-1 个结点(i≥1) m叉树第 i 层至多有 mi-1 个结点(i≥1)
3. 高度为h的二叉树至多有 2^ℎ − 1个结点(满二叉树),高度为h的m叉树至多有(mh^-1) /(m-1)个结点
#define MaxSize 100
struct TreNode{
ElemType value; //结点中的数据元素
bool isEmpty;//结点是否为空
};
TreeNode t[MaxSize];
定义一个长度为MaxSize的数组t,按照从上至下,从左至右的顺序依次存储完全二叉树中的各个结点。
^ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ^ | ^ | ^ | ^ |
---|
bool InitT(TreeNode L[]){
for(int i=0;i<MaxSize;i++){
t[i].isEmpty=true;
}
}
^ | 1 | 2 | 3 | ^ | 5 | 6 | 7 | ^ | ^ | ^ | 11 | 12 | ^ | ^ | ^ | ^ |
---|
这样就形成二叉树如图
6. 对于顺序存储而言,最坏的情况下,一个深度为k且只有k个结点的单支树需要长度为2k-1的一位数组
typedef struct BiTNood{
ElemType value; //结点中的数据元素
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
注意:n个结点的二叉链表共有n+1个空链域,因此可以用于构造线索二叉树
struct ElemType{
int value;
};
typedef struct BiTNood{
ElemType value; //结点中的数据元素
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//定义一个空树
BiTree root=NULL;
//插入根节点
root = (BiTree)malloc(sizeof(BiTNode));
root->data={1};
root->lchild=NULL;
root->rchild=Null;
//插入新结点
BiTNode *p=(BiTNode *)malloc(sizeif(BiTNode));
p->data={2};
p->lchild=NULL;
p->rchild=NULL;
root->lchild=p;
问题是:很难找到父节点(寻找子结点简单)
typedef struct BiTNood{
ElemType value; //结点中的数据元素
struct BiTNode *lchild,*rchild;
struct BiTNode *parent;
}BiTNode,*BiTree;
先序:根左右(前缀表达式)
中序:左根右(中缀表达式)
后序:左右根(后缀表达式)
(1)规则:想访问根节点后访问左结点,右结点
(2)代码思想:
① 如果二叉树为空,那就什么也不做
② 如果二叉树非空,先序遍历左子树,访问根节点,先序遍历右子树
(3)代码:
typedef struct BiTNood{
ElemType value; //结点中的数据元素
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
void InOrder(BiTree T){
if(T!=NULL){
visit(T);
InOrder(T->lchild);
InOrder(T->rchild);
}
}
(4)分析:①空间复杂度:O(h)
② 每一个结点都会在第一次访问时处理
③每一个结点都会路过三次
(1)代码实现:
if(T!=NULL){
InOrder(T->lchild);
visit(T);
InOrder(T->rchild);
}
(2)分析:①只有在第二次路过结点时才会访问 ②空间复杂度 O(h)
(1)代码实现:
if(T!=NULL){
InOrder(T->lchild);
InOrder(T->rchild);
visit(T);
}
(2)分析:①只有在第三次路过结点时才会访问 ②空间复杂度 O(h)
void preorder1(BiTree root)
{
BiTree s[MAX_TREE_SIZE],p;
int top;
top=0;
p=root;
while(p||top>0)
{
while(p)
{
cout<<p->data<<" ";
top++;
s.[top]=p;
p=p->lchild;
}
if(top>0)
{
p=s[top];
top--;
p=p->rchild;
}
}
}
前中后都差不多,不过是顺序变化
//;二叉树的顺序储存结构
#define MAX_TREE 10000;
typedef Telemtype SiTree[MAX_PATH];
SiTree bT;
void leveltree(BiTree *t)
{
BiTree s[100],p;
int front rear;
front=rear=0;
p=t;
if(t!=NULL)
{
rear++;
s[rear]=p;
while(front!=rear)
{ front++;
p=s[front];
count<<p->data;
if(p->lchild)
{
rear++;
s[rear]=p->lchild;
}
if(p->rchild)
{
rear++;
s[rear]=p->rchild;
}
}
}
}
typedef struct Bnode /*定义二叉树存储结构*/
{ char data;
struct Bnode *lchild,*rchild;
}Bnode,*Btree;
void Createtree(Btree &T) /*创建二叉树函数*/
{
//按先序次序输入二叉树中结点的值(一个字符),创建二叉链表表示的二叉树T
char ch;
cin >> ch;
if(ch=='#')
T=NULL; //递归结束,建空树
else{
T=new Bnode;
T->data=ch; //生成根结点
Createtree(T->lchild); //递归创建左子树
Createtree(T->rchild); //递归创建右子树
}
return;
}
bool Leveltraverse(Btree T)
{
Btree p;
if(!T)
return false;
queue<Btree>Q; //创建一个普通队列(先进先出),里面存放指针类型
Q.push(T); //根指针入队
while(!Q.empty()) //如果队列不空
{
p=Q.front();//取出队头元素
Q.pop(); //队头元素出队
cout<<p->data<<"\t";
if(p->lchild)
Q.push(p->lchild); //左孩子指针入队
if(p->rchild)
Q.push(p->rchild); //右孩子指针入队
}
return true;
}
int main()
{ Btree mytree;
Createtree(mytree);//创建二叉树
Leveltraverse(mytree);//层次遍历二叉树
return 0;
}
void creatree(BiTree &t);
{
char ch;
count<<ch<<" ";
t->data=ch;
if(ch!='@')
{
t=new BiTNode;
creatree(t->lchild);
creatree(t->rchild);
}
else
t=NULL;
}
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
*lchild | ltag | data | rtag | *rchild |
---|
tag=0 的时候指向自己的左右孩子
tag=1 的时候指向的是自己的前后驱(说明没有孩子)
3. 画出线索二叉树
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
ThreadNode *pre = NULL;
//中序线索二叉树
void CreateInThread(ThreadTree T){
pre = NULL; //pre初始为NULL
if(T!=NULL){ //非空二叉树才可以进行线索化
InThread(T); //终须线索化二叉树
if(pre->rchild==NULL)
pre->rtag=1; //处理遍历的最后一结点
}
}
//中序遍历二叉树,一边遍历一边中序线索化
void InTread(ThreadTree T){
if(T!=NULL){
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void visit(ThreadNode *q){
// 建前驱线索
if (p->lchild==NULL)
{
p->ltag = 1;
p->lchild = pre;
}
// 建后继线索
if (pre->rchild==NULL&&pre!=NULL)
{
pre->rtag = 1;
pre->rchild = p;
}
pre = p;
}
void InTread(ThreadTree T){
if(T!=NULL){
visit(T);
if (T->ltag==0)
InThread(T->lchild);//只有左指针指向的的确是左孩子时才访问
InThread(T->rchild);
}
(1)寻找指定结点*p的中序后继next的思想
①若p->rtag==1,则next=p->rchild
② 若p->rtag==0,则说明指定结点有右孩子,那么为右子树最左边的最后一个结点(如果p的右孩子是个叶子结点,那next=p->rchild)
(2)代码
ThreadNode *Firstnode(ThreadNode *p){
//循环找到最左下结点(不一定是叶子结点,可能只是没有左孩子但是有右孩子)
while(p->ltag==0) p=p->lchild;
return p;
}
ThreadNode *Nextnode(ThreadNode *p){
//右子树中最左下的结点
if(p->rtag==0) return Firstnode(p->rchild);
//如果右边是孩子,那么就找右子树中最左下的结点
//如果不是,那么就是本身右指针指向的后驱
else return p->rchild;
}
(3)根据上述思路就可以利用线索二叉树进行非递归中序遍历,只需要加入如下代码:
void visit(ThreadNode *q){
// 建前驱线索
if (p->lchild==NULL)
{ p->ltag = 1; p->lchild = pre;
}
// 建后继线索
if (pre->rchild==NULL&&pre!=NULL)
{ pre->rtag = 1; pre->rchild = p;
}
pre = p;
}
void Inorder (ThreadNode *T){
for(ThreadNode *p = Firstnode(T);p!=NULL;p=Nextnode(p)){
visit(p);
}
}
空间复杂度:O(1)
(1)寻找中序遍历指定点*p的前驱结点
①若p->ltag==1,则next=p->lchild
② 若p->ltag==0,则说明指定结点有左孩子,那么为左子树最右边的最后一个结点(如果p的右孩子是个叶子结点,那next=p->lchild)
(2)代码:
ThreadNode *lastnode(ThreadNode *p){
while(p->rtag==0) p=p->rchild;
return p;
}
ThreadNode *Prenode(ThreadNode *p){
if(p->ltag==0) return Firstnode(p->lchild);
else return p->lchild;
}
void visit(ThreadNode *q){
if (p->lchild==NULL)
{ p->ltag = 1; p->lchild = pre;
}
if (pre->rchild==NULL&&pre!=NULL)
{ pre->rtag = 1; pre->rchild = p;
}
pre = p;
}
void Inorder (ThreadNode *T){
for(ThreadNode *p = lastnode(T);p!=NULL;p=Prenode(p)){
visit(p);
}
}
注意:利用这种方式进行的中序遍历是逆向的
在先序线索二叉树中找到指定结点p的先序后继next
(1)若是叶子结点右标志为1,则右链为线索,指向其后继,即若p->rtag==1,则next=p->rchild
(2)若p->rtag==0,则一定有右孩子
① 若p有左孩子,那么后继为左孩子
② 若p没有左孩子,那么后继为右孩子
(1) 若p->ltag==1,则next=p->lchild
(2)若p->ltag==0,则说明有左孩子,而先序顺序为根左右,则左右子树只可能是根的后继,进而是找不到前驱的,只能进行重新先序遍历,则需要进行建立三叉链表,如下:
① 如果能找到p的父节点,且p是左孩子,则前驱为该父节点
② 如果能找到p的父节点,且p是右孩子,且其左兄弟为空,则前驱为该父节点
③ 如果能找到p的父节点,且p是右孩子,且其左兄弟非空,则前驱为前面最后一个被遍历的结点
④ 如果不能找到p的父节点(即p为根结点),则p没有先序前驱。
(1) 若p->rtag==1,则next=p->rchild
(2)若p->rtag==0,则一定有右孩子,由于后序遍历顺序为左右根,所以没有办法在二叉链表结构中找到后继,则要么从头遍历,要么使用三叉链表,三叉链表思路为:
①如果可以找到p的父节点,且p是右孩子,则后续后继为父节点
②如果可以找到p的父节点,且p是左孩子,且右兄弟为空,则后续后继为父节点
③如果可以找到p的父节点,且p是左孩子,且右兄弟非空,则后续后继为右兄弟子树中第一个被后序遍历的结点
④如果p是根节点,没有父节点,则没有后续后继结点
在后续线索二叉树中找到指定结点*p的后续前驱pre
(1)若p->ltag==1,则next=p->lchild
(2)若p->ltag==0,则说明一定有左孩子,右孩子不得而知
① 假设有右孩子,则后序前驱为右孩子
② 假设没有右孩子,则后序前驱为左孩子
中序线索二叉树 | 先序线索二叉树 | 后序线索二叉树 | |
---|---|---|---|
找前驱 | √ | × | √ |
找后继 | √ | √ | × |
×的都可以用三叉链表或者从头开始遍历寻找的方法
typedef struct {
ElemType data;
int parent;
}PTNode;
typedef struct{
PTNode nodes[Max_TREE_SIZE];
int n;
}PTree;
下标 | data | parent |
---|---|---|
0 | A | -1 |
1 | B | 0 |
2 | C | 0 |
3 | D | 0 |
4 | E | 1 |
5 | F | 1 |
6 | G | 2 |
7 | H | 3 |
8 | I | 3 |
9 | J | 3 |
10 | K | 4 |
#define Max_TREE_SIZE 100
struct CTNode{
int child; //孩子结点在数组中的位置
struct CTNode *next;//下一个孩子
};
typedef struct{
ElemType data;
struct CTNode *firstChild;//第一个孩子
}CTBox;
typedef struct{
CTBox nodes[Max_TREE_SIZE];
int n,r;//节点数和根的位置
}CTree;
#define Max_TREE_SIZE 100
typedef struct CSNode{
ELemType data; //数据域
struct CSNode *firstchild,*nextsibling;//第一个孩子和右兄弟指针
}CSNode,*CSTree;
(1)思想:如果树不空的话,那么就先访问根结点,再依次对每棵子树进行先根遍历
(2)代码:
void PreOrder(TreeNode *R){
if(R!=NULL){
visit(R);//访问根结点
while(R还有下一个子树T){
PreOrder(T);//先根遍历下一棵子树
}
}
}
(3)示例:
发现:树的先根遍历序列与这个树对应的二叉树的先序序列相同
(1)代码实现:
void PostOrder(TreeNode *R){
if(R!=NULL){
while(R还有下一个子树T){
PostOrder(T);//后根遍历下一棵子树
}
visit(R);//访问根结点
}
}
(2)示例
(3)发现:树的先根遍历序列与这个树对应的二叉树的先序序列相同
(1)思想:
① 若树非空,则根结点入队
② 若队列非空,队头元素出队并访问,同时将该元素的孩子一次入队
③ 重复②直到队列为空
(类似于广度优先遍历)
森林是m(m≥0)棵互不相交的树的集合。每棵树去掉根结点后,其各个子树又组成森林
若森林为非空,则按如下规则进行遍历
① 访问森林中第一课树的根结点
② 先序遍历第一棵树中根结点的子树森林
③ 先序遍历除去第一棵树之后剩余的树构成的森林
效果等于依次对各个树进行先根遍历,也可以等同于转换为二叉树后的先序遍历
若森林为非空,则按如下规则进行遍历
①先序遍历第一棵树中根结点的子树森林
②访问森林中第一课树的根结点的
③ 先序遍历除去第一棵树之后剩余的树构成的森林
效果等于依次对各个树进行后根遍历,也可以等同于转换为二叉树后的中序遍历序列
树 | 森林 | 二叉树 |
---|---|---|
先根遍历 | 先序遍历 | 先序遍历 |
后根遍历 | 中序遍历 | 中序遍历 |
算法基本思想: 二叉树的深度应为其左、右子树深度的最大值加1。由此,需先分别求得左、右子树的深度,算法中“访问结点”的操作为:求得左、右子树深度的最大值,然后加1。
int findtree(BiTree *t)
{ int l,r;
if(t==NULL) return 0;
else
{
l=findtree(t->lchhild);
r=findtree(t->rchild);
if(l<r) return r+1;
else return l+1;
}
}
int addTnode(Bitree *t)
{
if(t=NULL) return 0;
else
if(t->rchild==NULL&&t->lchild==NULL)
n++;
addTnode(t->rchild);
addTnode(t->lchild);
return n;
}
void copytree(BiTree t1,BiTree t2)
{ if(t1==NULL)
t2=NULL;
else {
t2=new BiTNode;
t2->data=t1->data;
t2->lchild=NULL;
t2->rchild=NULL;
copytree(t1->rchild,t2->rchild);
copytree(t1->lchild,t2->lchild);
}
}
#define _CRT_SECURE_NO_WARNINGS // VS忽略警告,其它应该不需要
#include
#include
#include
#include
#define MAX_SIZE 128
#define STR_SIZE 1024
typedef struct Node { // 定义二叉链
char data; // 数据元素
struct Node* lchild; // 指向左孩子节点
struct Node* rchild; // 指向右孩子节点
} BTNode; // struct Node 的别名
typedef struct Quene { // 定义顺序队
int front; // 队头指针
int rear; // 队尾指针
BTNode* data[MAX_SIZE]; // 存放队中元素
} SqQueue; // struct Queue 的别名
/**
* 队列函数
*/
void initQueue(SqQueue** q); // 初始化队列
bool emptyQueue(SqQueue* q); // 判断队列空
bool enQueue(SqQueue* q, BTNode* node); // 入队
bool deQueue(SqQueue* q, BTNode** node); // 出队
/**
* 二叉树函数
*/
// void createBTNode2(BTNode** BT); // 创建二叉树
int createBTNode(BTNode** BT, char* str, int n); // 创建二叉树
void preOrder(BTNode* BT); // 前序遍历
void inOrder(BTNode* BT); // 中序遍历
void postOrder(BTNode* BT); // 后序遍历
void levelOrder(BTNode* BT); // 层次遍历
/**
* 画树函数
*/
void draw_level(BTNode* node, bool left, char* str); // 画分支
void draw(BTNode* root); // 画根节点
/***************************************************************************
* @date 2019/12/08
* @brief 层次遍历二叉树
* @param BT 二叉树根节点
***************************************************************************/
void levelOrder(BTNode* BT) {
SqQueue* q; // 定义队列
initQueue(&q); // 初始化队列
if (BT != NULL) { // 根节点指针进队列
enQueue(q, BT);
}
// 一层一层的把节点存入队列,当没有孩子节点时就不再循环
while (!emptyQueue(q)) { // 队不为空循环
deQueue(q, &BT); // 出队时的节点
printf("%c", BT->data); // 输出节点存储的值
if (BT->lchild != NULL) { // 有左孩子时将该节点进队列
enQueue(q, BT->lchild);
}
if (BT->rchild != NULL) { // 有右孩子时将该节点进队列
enQueue(q, BT->rchild);
}
}
}
int main() {
// 例子:ABDH###E##CF##G##
BTNode* BT;
printf("请输入字符串:");
char* str = (char*)malloc(sizeof(char) * STR_SIZE);
scanf("%s", str);
if (strlen(str) == createBTNode(&BT, str, 0)) {
printf("二叉树建立成功\n");
}
// printf("请输入字符串:");
// createBTNode2(&BT);
// draw(BT);
printf("\n先序遍历结果:");
preOrder(BT);
printf("\n中序遍历结果:");
inOrder(BT);
printf("\n后序遍历结果:");
postOrder(BT);
printf("\n层序遍历结果:");
levelOrder(BT);
return 0;
}
// 初始化队列
void initQueue(SqQueue** q) {
if (!((*q) = (SqQueue*)malloc(sizeof(SqQueue)))) {
printf("内存分配失败!");
exit(-1);
}
(*q)->front = (*q)->rear = -1; // 置 -1
}
// 判断队列是否为空
bool emptyQueue(SqQueue* q) {
// 首指针和尾指针相等,说明为空。空-返回真,不空-返回假
if (q->front == q->rear) {
return true;
}
return false;
}
// 进队列
bool enQueue(SqQueue* q, BTNode* node) {
// 判断队列是否满了。满(插入失败)-返回假,不满(插入成功)-返回真
if (q->rear == MAX_SIZE - 1) {
return false;
}
q->rear++; // 头指针加 1
q->data[q->rear] = node; // 传值
return true;
}
// 出队列
bool deQueue(SqQueue* q, BTNode** node) {
// 判断是否空了。空(取出失败)-返回假,不空(取出成功)-返回真
if (q->front == q->rear) {
return false;
}
q->front++; // 尾指针加 1
*node = q->data[q->front]; // 取值
return true;
}
// 创建二叉树
int createBTNode(BTNode** BT, char* str, int n) {
char ch = str[n++]; // 把第 n 个字符赋给ch,方便后面判断,字符下标后移
if (ch != '\0') { // 如果 ch 不等于结束符就继续创建,否则就结束
if (ch == '#') { // 以 # 号代表 NULL,下面没有了
*BT = NULL;
} else {
if (!(*BT = (BTNode*)malloc(sizeof(BTNode)))) {
printf("内存分配失败!");
exit(-1);
} else {
(*BT)->data = ch;
n = createBTNode(&((*BT)->lchild), str, n); // 左递归创建
n = createBTNode(&((*BT)->rchild), str, n); // 右递归创建
}
}
}
// 返回 n,记录字符串使用到哪里了
return n;
}
// 创建二叉树
// void createBTNode2(BTNode** BT) {
// char ch;
// ch = getchar();
// if (ch == '#') {
// *BT = NULL;
// } else {
// if (!(*BT = (BTNode*)malloc(sizeof(BTNode)))) {
// printf("内存分配失败!");
// return;
// } else {
// (*BT)->data = ch;
// createBTNode2(&((*BT)->lchild)); // 分配成功则接着建立左子树和右子树
// createBTNode2(&((*BT)->rchild));
// }
// }
// }
// 先序遍历
void preOrder(BTNode* BT) {
if (BT != NULL) { // 判断不为空
printf("%c", BT->data); // 访问根节点
preOrder(BT->lchild); // 递归,先序遍历左子树
preOrder(BT->rchild); // 递归,先序遍历右子树
}
}
// 中序遍历
void inOrder(BTNode* BT) {
if (BT != NULL) {
inOrder(BT->lchild);
printf("%c", BT->data);
inOrder(BT->rchild);
}
}
// 后序遍历
void postOrder(BTNode* BT) {
if (BT != NULL) {
postOrder(BT->lchild);
postOrder(BT->rchild);
printf("%c", BT->data);
}
}
/*****************************************************************************
* @date 2020/4/19
* @brief 水平画树
* @param node 二叉树节点
* @param left 判断左右
* @param str 可变字符串
*****************************************************************************/
void draw_level(BTNode* node, bool left, char* str) {
if (node->rchild) {
draw_level(node->rchild, false, strcat(str, (left ? "| " : " ")));
}
printf("%s", str);
printf("%c", (left ? '\\' : '/'));
printf("-----");
printf("%c\n", node->data);
if (node->lchild) {
draw_level(node->lchild, true, strcat(str, (left ? " " : "| ")));
}
// " " : "| " 长度为 6
str[strlen(str) - 6] = '\0';
}
/*****************************************************************************
* @date 2020/4/19
* @brief 根节点画树
* @param root 二叉树根节点
*****************************************************************************/
void draw(BTNode* root) {
char str[STR_SIZE];
memset(str, '\0', STR_SIZE);
/**
* 1. 在 windows 下,下面是可执行的
* 2. 在 Linux 下,执行会报 Segmentation fault
* 需要使用中间变量
*/
if (root->rchild) {
draw_level(root->rchild, false, str);
}
printf("%c\n", root->data);
if (root->lchild) {
draw_level(root->lchild, true, str);
}
}