一.二叉树的链式存储(以递归的形式表示出来)
1.结构体(此处用字符型作为二叉树的数据域类型)
//定义树的结构体
typedef struct BiTNode
{
char data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
2.定义串的结构体
//定义串的结构体
typedef struct
{
char ch[Maxsize];
int length;
}SString;
3.串执行的操作
//串赋值操作
void StrAssign(SString &T,char *chars)
{
T.length = strlen(chars);
T.ch[0] = T.length;
for(int i = 1; i <= Maxsize; ++i)
{
T.ch[i] = *(chars++);
}
}
//串遍历输出操作
void StrPrint(SString &T)
{
int i;
for(i = 1; i <= T.length; ++i)
{
printf("%c",T.ch[i]);
}
printf("\n");
}
4.初始化二叉树和创建二叉树
创建二叉树类似于前序遍历,通过 '#' 为空,然后用先序遍历来表示出二叉树,将结果作为字符串用于在程序中表示出来
以前序遍历表示出来为:AB##CD##E##
//初始化二叉树
void InitBiTree(BiTree &T)
{
T = NULL;
printf("初始化成功!\n");
}
//创建二叉树(类似于前序遍历来创建二叉树)
void CreatBiTree(BiTree &T,SString &S)
{
char s = S.ch[h++];//定义一个变量s用来存储字符串的元素,然后作为判断是否为#;S.ch[h++]先将
S.ch[h]的值赋值给s,然后S.ch[h]就为字符串的下一个元素;如刚才是
S.ch[1],此时就是S.ch[2]
if(s == '#')//如果元素为#,则说明该结点元素为空!表明该结点为一个空结点
T = NULL;//因为指向的结点为空结点,自然指针指向空,然后栈会弹出改行代码,继续执行if(s
== '#')代码
else
{
T = (BiTree)malloc(sizeof(BiTNode));//如果不是#,就会动态分配内存作为二叉树的结点,此时
指针T指向该结点
if(T == NULL)
{
printf("动态内存分配失败,结束程序!\n");
exit(-1);
}
else
{
T->data = s;//将字符串中的元素赋值给结点的数据域
CreatBiTree(T->lchild,S);//如刚才的例子,因为函数间用的是地址的形式,所以此时S指向ch
为:S.ch[2],然后进行递归
CreatBiTree(T->rchild,S);//原理一致
}
}
}
5.遍历输出(以王道视频讲解总结)
前序遍历的过程:脑补出空结点,从根节点出发,画一条路,如果左边还有没走的路,优先往左边走,走到路的尽头(空结点)就往回走;如果左边没路了,就往右边走;如果左右都没路了,则往上面走。先序遍历就是第一次路过时访问结点。结果:ABDGECF
中序遍历的过程:和上面一致,只不过是第二次路过时访问结点。结果:DGBEAFC
后序遍历的过程:也是和上面一致,只不过是第三次路过时访问结点。结果:GDEBFCA
遍历代码:
//前序遍历
void PreOrder(BiTree T)
{
if(T != NULL)
{
Visit(T);//访问根节点
PreOrder(T->lchild);//先序遍历左子树
PreOrder(T->rchild);//先序遍历右子树
}
}
//中序遍历
void InOrder(BiTree T)
{
if(T != NULL)
{
InOrder(T->lchild);//中序遍历左子树
Visit(T);//访问根节点
InOrder(T->rchild);//中序遍历右子树
}
}
//后序遍历
void PostOrder(BiTree T)
{
if(T != NULL)
{
PostOrder(T->lchild);//后续遍历左子树
PostOrder(T->rchild);//后序遍历右子树
Visit(T);//访问根节点
}
}
6.层次遍历
算法思想:要想进行层次遍历,需要借助一个队列。首先二叉树根节点入队,然后出队,访问出队结点,若它有左子树,则将左子树根节点入队;若它有右子树,则将右子树的根节点入队。完成入队后出队,访问出队结点的左右子树...反复如此,直至队列为空。
队列结构体
//定义队列的结构体
typedef struct LinkNode
{
BiTNode *data;//为了节省空间,并不是把树的结点全部都压进去,而是压入树的指针,树的指针类型是BiTNode,所以数据域也是BiTNode类型
struct LinkNode *next;
}LinkNode;
typedef struct
{
LinkNode *front,*rear;
}LinkQueue;
队列的操作
//初始化队列
void InitQueue(LinkQueue &Q)
{
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;
}
//入队
void EnQueue(LinkQueue &Q, BiTree &s)
{
LinkNode *p = (LinkNode*)malloc(sizeof(LinkNode));
p->data = s;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
}
//出队
bool DeQueue(LinkQueue &Q,BiTree &s)
{
if(Q.front == Q.rear)//这是判断队列为空的条件
return false;
LinkNode *p= Q.front->next;//定义一个指针指向Q.front->next的这个地址的结点
s = p->data;//因为层次遍历输出,入列的是BiTNode类型的地址,所以将地址存于s中
Q.front->next = p->next;//使p指向结点的后继节点挂在头结点的后面
if(p == Q.rear)//因为此时Q.front->next已经改变,所以要表示头结点后面就只有一个结点这种特殊
情况只能用p == Q.rear表示,因为最开始就令p = Q.front,该条件没有改变
Q.rear = Q.front;
free(p);
return true;
}
//判断队列为空
bool IsEmpty(LinkQueue &Q)
{
if(Q.front == Q.rear)
return true;
else
return false;
}
层次遍历
//层次遍历
void LevelOrder(LinkQueue &Q, BiTree &T)
{
BiTree p = T;//定义一个指针,指向树的根节点
EnQueue(Q,p); //先将指向根节点的指针压入队列中
while(!IsEmpty(Q))//判断空,作为循环结束条件
{
DeQueue(Q,p);//出队
Visit(p);//访问出队指针指向的结点
if(p->lchild != NULL)//判断刚出队指针指向的结点是否有左孩子
EnQueue(Q,p->lchild);//如果有就将指向其左孩子结点的指针入队
if(p->rchild != NULL)//判断刚出队指针指向的结点是否有右孩子
EnQueue(Q,p->rchild);//如果有就将指向其右孩子结点的指针入队
}
}
运行结果
完整代码如下:
#include
#include
#include
#include
#define Maxsize 11
int h = 1;
//定义树的结构体
typedef struct BiTNode
{
char data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//定义队列的结构体
typedef struct LinkNode
{
BiTNode *data;//为了节省空间,并不是把树的结点全部都压进去,而是压入树的指针,树的指针类型是BiTNode,所以数据域也是BiTNode类型
struct LinkNode *next;
}LinkNode;
typedef struct
{
LinkNode *front,*rear;
}LinkQueue;
//定义串的结构体
typedef struct
{
char ch[Maxsize];
int length;
}SString;
//队列的函数说明
void InitQueue(LinkQueue &Q);
void EnQueue(LinkQueue &Q, BiTree &s);
bool DeQueue(LinkQueue &Q, BiTree &s);
bool IsEmpty(LinkQueue &Q);
//二叉树的函数说明
void InitBiTree(BiTree &T);
void CreatBiTree(BiTree &T,SString &S);
void Visit(BiTree T);
void PreOrder(BiTree T);
void InOrder(BiTree T);
void PostOrder(BiTree T);
void LevelOrder(LinkQueue &Q, BiTree &T);
//字符串的函数说明
void StrAssign(SString &T,char *chars);
void StrPrint(SString &T);
int main(void)
{
printf("定义一个队列Q\n");
LinkQueue Q;
printf("初始化队列Q\n");
InitQueue(Q);
printf("定义一个字符串S\n");
SString S;
printf("将字符串的元素赋值给S\n");
StrAssign(S,"AB##CD##E##");
printf("遍历输出字符串S\n");
StrPrint(S);
printf("定义一个树T\n");
BiTree T;
printf("初始化树T\n");
InitBiTree(T);
printf("创造树T\n");
CreatBiTree(T,S);
printf("先序遍历树T\n");
PreOrder(T);
printf("\n");
printf("中序遍历树T\n");
InOrder(T);
printf("\n");
printf("后序遍历树T\n");
PostOrder(T);
printf("\n");
printf("层次遍历树T\n");
LevelOrder(Q,T);
return 0;
}
//初始化队列
void InitQueue(LinkQueue &Q)
{
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;
}
//入队
void EnQueue(LinkQueue &Q, BiTree &s)
{
LinkNode *p = (LinkNode*)malloc(sizeof(LinkNode));
p->data = s;
p->next = NULL;
Q.rear->next = p;
Q.rear = p;
}
//出队
bool DeQueue(LinkQueue &Q,BiTree &s)
{
if(Q.front == Q.rear)//这是判断队列为空的条件
return false;
LinkNode *p= Q.front->next;//定义一个指针指向Q.front->next的这个地址的结点
s = p->data;//因为层次遍历输出,入列的是BiTNode类型的地址,所以将地址存于s中
Q.front->next = p->next;//使p指向结点的后继节点挂在头结点的后面
if(p == Q.rear)//因为此时Q.front->next已经改变,所以要表示头结点后面就只有一个结点这种特殊情况只能用p == Q.rear表示,因为最开始就令p = Q.front,该条件没有改变
Q.rear = Q.front;
free(p);
return true;
}
//判断队列为空
bool IsEmpty(LinkQueue &Q)
{
if(Q.front == Q.rear)
return true;
else
return false;
}
//串赋值操作
void StrAssign(SString &T,char *chars)
{
T.length = strlen(chars);
T.ch[0] = T.length;
for(int i = 1; i <= Maxsize; ++i)
{
T.ch[i] = *(chars++);
}
}
//串遍历输出操作
void StrPrint(SString &T)
{
int i;
for(i = 1; i <= T.length; ++i)
{
printf("%c",T.ch[i]);
}
printf("\n");
}
//初始化二叉树
void InitBiTree(BiTree &T)
{
T = NULL;
printf("初始化成功!\n");
}
//创建二叉树(类似于前序遍历来创建二叉树)
void CreatBiTree(BiTree &T,SString &S)
{
char s = S.ch[h++];//定义一个变量s用来存储字符串的元素,然后作为判断是否为#;S.ch[h++]先将S.ch[h]的值赋值给s,然后S.ch[h]就为字符串的下一个元素;如刚才是S.ch[1],此时就是S.ch[2]
if(s == '#')//如果元素为#,则说明该结点元素为空!表明该结点为一个空结点
T = NULL;//因为指向的结点为空结点,自然指针指向空,然后栈会弹出改行代码,继续执行if(s == '#')代码
else
{
T = (BiTree)malloc(sizeof(BiTNode));//如果不是#,就会动态分配内存作为二叉树的结点,此时指针T指向该结点
if(T == NULL)
{
printf("动态内存分配失败,结束程序!\n");
exit(-1);
}
else
{
T->data = s;//将字符串中的元素赋值给结点的数据域
CreatBiTree(T->lchild,S);//如刚才的例子,因为函数间用的是地址的形式,所以此时S指向ch为:S.ch[2],然后进行递归
CreatBiTree(T->rchild,S);//原理一致
}
}
}
//访问结点
void Visit(BiTree T)
{
printf("%c",T->data);
}
//前序遍历
void PreOrder(BiTree T)
{
if(T != NULL)
{
Visit(T);//访问根节点
PreOrder(T->lchild);//先序遍历左子树
PreOrder(T->rchild);//先序遍历右子树
}
}
//中序遍历
void InOrder(BiTree T)
{
if(T != NULL)
{
InOrder(T->lchild);//中序遍历左子树
Visit(T);//访问根节点
InOrder(T->rchild);//中序遍历右子树
}
}
//后序遍历
void PostOrder(BiTree T)
{
if(T != NULL)
{
PostOrder(T->lchild);//后续遍历左子树
PostOrder(T->rchild);//后序遍历右子树
Visit(T);//访问根节点
}
}
//层次遍历
void LevelOrder(LinkQueue &Q, BiTree &T)
{
BiTree p = T;//定义一个指针,指向树的根节点
EnQueue(Q,p); //先将指向根节点的指针压入队列中
while(!IsEmpty(Q))//判断空,作为循环结束条件
{
DeQueue(Q,p);//出队
Visit(p);//访问出队指针指向的结点
if(p->lchild != NULL)//判断刚出队指针指向的结点是否有左孩子
EnQueue(Q,p->lchild);//如果有就将指向其左孩子结点的指针入队
if(p->rchild != NULL)//判断刚出队指针指向的结点是否有右孩子
EnQueue(Q,p->rchild);//如果有就将指向其右孩子结点的指针入队
}
}