目录
一、建树
1.定义树
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
2.建树函数
void CreateTree(BiTree& Root)
{
TElemType c;
if ((c = getchar()) != '\n')
{
Root = (BiTree)malloc(sizeof(BiTNode));
Root->data = c;
CreateTree(Root->lchild);
CreateTree(Root->rchild);
}
else
Root = NULL;
}
如果要用上面的代码建立下面这样的二叉树
则输入应该为AB\nC\n\nDE\n\nF\n\n
二、三种遍历的递归版
1.先序遍历递归函数
代码理解起来很容易,即在遍历左子树和右子树之前将数据读取并输出,后面的两种只是将语句顺序改变一下即可
void PreTraverse_DG(BiTree Root)
{
if (Root == NULL)return;
putchar(Root->data);
PreTraverse_DG(Root->lchild);
PreTraverse_DG(Root->rchild);
}
2.中序遍历递归函数
void MiddleTraverse_DG(BiTree Root)
{
if (Root != NULL)
{
MiddleTraverse_DG(Root->lchild);
putchar(Root->data);
MiddleTraverse_DG(Root->rchild);
}
else return;
}
3.后序遍历递归函数
void AfterTraverse_DG(BiTree Root)
{
if (Root == NULL)return;
AfterTraverse_DG(Root->lchild);
AfterTraverse_DG(Root->rchild);
putchar(Root->data);
}
4.总代码和运行结果展示
总代码:
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
//创建树
void CreateTree(BiTree& Root)
{
TElemType c;
if ((c = getchar()) != '\n')
{
Root = (BiTree)malloc(sizeof(BiTNode));
Root->data = c;
CreateTree(Root->lchild);
CreateTree(Root->rchild);
}
else Root = NULL;
}
//先序遍历(递归)
void PreTraverse_DG(BiTree Root)
{
if (Root == NULL)return;
putchar(Root->data);
PreTraverse_DG(Root->lchild);
PreTraverse_DG(Root->rchild);
}
//中序遍历(递归)
void MiddleTraverse_DG(BiTree Root)
{
if (Root == NULL)return;
MiddleTraverse_DG(Root->lchild);
putchar(Root->data);
MiddleTraverse_DG(Root->rchild);
}
//后序遍历(递归)
void AfterTraverse_DG(BiTree Root)
{
if (Root == NULL)return;
AfterTraverse_DG(Root->lchild);
AfterTraverse_DG(Root->rchild);
putchar(Root->data);
}
int main()
{
BiTree Root;
CreateTree(Root);
printf("\npre_DG:");
PreTraverse_DG(Root);
printf("\nmiddle_DG:");
MiddleTraverse_DG(Root);
printf("\nafter_DG:");
AfterTraverse_DG(Root);
return 0;
}
运行结果:
三、先序遍历非递归(使用栈实现)
虽然递归和非递归的时间复杂度和空间复杂度相同,但是递归调用需要系统自己在递归的过程中去开辟和销毁堆栈中的空间,比自己创建一个栈要更加费时,因此这里给出了非递归的写法。
1.非递归所要用到的数据结构——栈
(1)栈的定义方式
由于要存储的是树的节点,因此定义方式应为下面代码所示
typedef struct {
BiTree base;
BiTree top;
int stacksize;
}SqStack;
(2)栈的基本操作
这里不再赘述,直接给出代码
需要注意的是,在进行S.top=Root这步操作的时候、S.top是是已经分配好的空间因此不能直接进行赋值操作,否则S.top的地址改变,系统报错,应该将Root的三个值以此赋给S.top
int InitStack(SqStack& S)
{
S.base = (BiTree)malloc(STACK_INIT_SIZE * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return 1;
}
int Push(SqStack& S, BiTree Root)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (BiTree)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
S.top->data = Root->data;
S.top->lchild = Root->lchild;
S.top->rchild = Root->rchild;
S.top++;
return 1;
}
BiTree Pop(SqStack& S)
{
if (S.top == S.base)return NULL;
S.top--;
return S.top;
}
int isEmpty(SqStack S)
{
return S.top == S.base;
}
2.核心函数
先序遍历非递归的主要步骤是
(1)创建一个存储树的结点的栈
(2)当栈不为空或Root不为NULL时,进行以下的操作
(3)如果Root不为空,则先打印其数据,再将其指向其左子树
(4)如果Root为空,则将栈顶元素弹出,并让其指向其右子树
void PreTraverse(BiTree Root)
{
SqStack S;
InitStack(S);
while (Root != NULL || !isEmpty(S))
{
if (Root != NULL)
{
putchar(Root->data);
Push(S, Root);
Root = Root->lchild;
}
else
{
Root = Pop(S);
Root = Root->rchild;
}
}
}
3.总代码及结果展示
(1)总代码
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
typedef struct {
BiTree base;
BiTree top;
int stacksize;
}SqStack;
int InitStack(SqStack& S)
{
S.base = (BiTree)malloc(STACK_INIT_SIZE * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return 1;
}
int Push(SqStack& S, BiTree Root)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (BiTree)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
S.top->data = Root->data;
S.top->lchild = Root->lchild;
S.top->rchild = Root->rchild;
S.top++;
return 1;
}
BiTree Pop(SqStack& S)
{
if (S.top == S.base)
return NULL;
S.top--;
return S.top;
}
int isEmpty(SqStack S)
{
return S.top == S.base;
}
//创建树
void CreateTree(BiTree& Root)
{
TElemType c;
if ((c = getchar()) != '\n')
{
Root = (BiTree)malloc(sizeof(BiTNode));
Root->data = c;
CreateTree(Root->lchild);
CreateTree(Root->rchild);
}
else
Root = NULL;
}
//先序遍历(非递归)
void PreTraverse(BiTree Root)
{
SqStack S;
InitStack(S);
while (Root != NULL || !isEmpty(S))
{
if (Root != NULL)
{
putchar(Root->data);
Push(S, Root);
Root = Root->lchild;
}
else
{
Root = Pop(S);
Root = Root->rchild;
}
}
}
int main()
{
BiTree Root;
CreateTree(Root);
printf("\npre:");
PreTraverse(Root);
return 0;
}
(2)运行结果
四、中序遍历非递归(使用栈实现)
与先序遍历相同,在逻辑上只是将先打印数据再遍历左子树(DLR)改成了先遍历左子树在打印数据(LDR),在代码上可以分为以下几个步骤:
(1)创建一个存储树的结点的栈
(2)当栈不为空或Root不为NULL时,进行以下的操作
(3)如果Root不为空,则将其指向其左子树
(4)如果Root为空,则将栈顶元素弹出,将其打印下来,再让其指向其右子树
总代码:
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
typedef struct {
BiTree base;
BiTree top;
int stacksize;
}SqStack;
int InitStack(SqStack& S)
{
S.base = (BiTree)malloc(STACK_INIT_SIZE * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return 1;
}
int Push(SqStack& S, BiTree Root)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (BiTree)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
S.top->data = Root->data;
S.top->lchild = Root->lchild;
S.top->rchild = Root->rchild;
S.top++;
return 1;
}
BiTree Pop(SqStack& S)
{
if (S.top == S.base)
return NULL;
S.top--;
return S.top;
}
int isEmpty(SqStack S)
{
return S.top == S.base;
}
//创建树
void CreateTree(BiTree& Root)
{
TElemType c;
if ((c = getchar()) != '\n')
{
Root = (BiTree)malloc(sizeof(BiTNode));
Root->data = c;
CreateTree(Root->lchild);
CreateTree(Root->rchild);
}
else
Root = NULL;
}
//中序遍历(非递归)
void MiddleTraverse(BiTree Root)
{
SqStack S;
InitStack(S);
while (Root != NULL || !isEmpty(S))
{
if (Root != NULL)
{
Push(S, Root);
Root = Root->lchild;
}
else
{
Root = Pop(S);
putchar(Root->data);
Root = Root->rchild;
}
}
}
int main()
{
BiTree Root;
CreateTree(Root);
printf("\nmiddle:");
MiddleTraverse(Root);
return 0;
}
运行结果:
六、后序遍历的非递归实现(使用栈实现)
后序遍历的非递归有两种实现方法,一种是直接按照LRD的方法进行实现,不过在实际操作上有些麻烦,另外一种方法是将先序遍历(DLR)改为(DRL)后逆序输出,下面详细介绍这两种方法
1.LRD
后序遍历的代码逻辑比较繁琐,分为以下几个步骤
(1)创建一个存储树的结点的栈(BiNode数组实在调不出来,换成了BiTree数组)
(2)当栈不为空或Root不为NULL时,现将栈顶元素赋给Root再进行以下的操作
(4)如果Root不为空,则将其入栈,并使其指向它的左子树
(3)如果Root为空,再进行一次判断,如果Root没有右子树或其右子树在上一步已经被遍历过,则将栈顶元素出栈,打印该栈顶指针所指结构体的data值,将Root赋值给pre(这里的pre是为了记录“上一步操作”,方便下一次遍历的时候检查是否其右子树被遍历过),然后将Root赋值为NULL(这一步非常重要!在进行遍历的时候因为要返回遍历其父节点(因为根据前面的条件可以知道其右子树已经被遍历或右子树为NULL)而只有Root为空的时候才会进入此操作)。否则遍历其右子树
总代码:
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
typedef struct {
BiTree* base;
int top;
int stacksize;
}SqStack;
//创建树
void CreateTree(BiTree& Root)
{
TElemType c;
if ((c = getchar()) != '\n')
{
Root = (BiTree)malloc(sizeof(BiTNode));
Root->data = c;
CreateTree(Root->lchild);
CreateTree(Root->rchild);
}
else Root = NULL;
}
//后序遍历(非递归)
void AfterTraverse(BiTree Root)
{
SqStack S;
BiTree pre = NULL;
S.base = (BiTree*)malloc(STACK_INIT_SIZE * sizeof(BiTree));
S.stacksize = STACK_INIT_SIZE;
S.top = 0;
while (Root || S.top)
{
if (Root)
{
S.base[S.top++] = Root;
Root = Root->lchild;
}
else
{
Root = S.base[S.top - 1];
if (!Root->rchild || Root->rchild == pre)
{
Root = S.base[--S.top];
putchar(Root->data);
pre = Root;
Root = NULL;
}
else Root = Root->rchild;
}
}
}
int main()
{
BiTree Root;
CreateTree(Root);
printf("\nafter:");
AfterTraverse(Root);
return 0;
}
运行结果:
2.DLR->DRL->LDR
总代码:
#include<stdio.h>
#include<stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
typedef struct {
BiTree base;
BiTree top;
int stacksize;
}SqStack;
int InitStack(SqStack& S)
{
S.base = (BiTree)malloc(STACK_INIT_SIZE * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return 1;
}
int Push(SqStack& S, BiTree Root)
{
if (S.top - S.base >= S.stacksize)
{
S.base = (BiTree)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(BiTNode));
if (!S.base)
return -1;
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
S.top->data = Root->data;
S.top->lchild = Root->lchild;
S.top->rchild = Root->rchild;
S.top++;
return 1;
}
BiTree Pop(SqStack& S)
{
if (S.top == S.base)
return NULL;
S.top--;
return S.top;
}
BiTree GetTop(SqStack S)
{
if (S.top == S.base)
return NULL;
return S.top - 1;
}
bool isEmpty(SqStack S)
{
return S.top == S.base;
}
//创建树
void CreateTree(BiTree& Root)
{
TElemType c;
if ((c = getchar()) != '\n')
{
Root = (BiTree)malloc(sizeof(BiTNode));
Root->data = c;
CreateTree(Root->lchild);
CreateTree(Root->rchild);
}
else Root = NULL;
}
void AfterTraverse_Pre(BiTree Root)
{
SqStack S;
char stack[100];
int cnt = 0;
InitStack(S);
while (Root != NULL || !isEmpty(S))
{
if (Root != NULL)
{
stack[cnt++]=Root->data;
Push(S, Root);
Root = Root->rchild;
}
else
{
Root = Pop(S);
Root = Root->lchild;
}
}
for (int i = cnt - 1; i >= 0; i--)
{
printf("%c", stack[i]);
}
}
int main()
{
BiTree Root;
CreateTree(Root);
printf("\nafter:");
AfterTraverse_Pre(Root);
return 0;
}
运行结果:同上
七、层序遍历(队列实现)
1.层序遍历用到的数据结构——队列
(1)定义
这里采用的是循环队列
typedef struct {
BiTree* base;
int front;
int rear;
int QueueSize;
}Queue;
(2)队列的相关操作
主要用到的三个函数为初始化,入队和出队
void InitQueue(Queue& Q)
{
Q.base = (BiTree*)malloc(sizeof(BiTree) * MAXSIZE);
Q.front = Q.rear = 0;
Q.QueueSize = MAXSIZE;
}
void EnQueue(Queue& Q, BiTree e)
{
if ((Q.rear + 1) % MAXSIZE == Q.front)return;
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXSIZE;
}
void DeQueue(Queue& Q, BiTree& e)
{
if (Q.front == Q.rear)return;
e = Q.base[Q.front];
Q.front = (Q.front + 1) % MAXSIZE;
}
2.核心函数
主函数的主要思想是广度优先搜索(BFS),主要步骤为:
(1)初始化一个队列,用于存储树的结点
(2)将根节点加入队列
(3)循环遍历,当队列不为空时进行以下几步操作
(4)将队头元素弹出,遍历这个节点,再将其左孩子和右孩子加入队列。
具体代码如下:
//层序遍历
void RankTraverse(BiTree Root)
{
Queue Q;
InitQueue(Q);
EnQueue(Q, Root);
while (!(Q.rear == Q.front))
{
DeQueue(Q, Root);
putchar(Root->data);
if(Root->lchild)EnQueue(Q, Root->lchild);
if(Root->rchild)EnQueue(Q, Root->rchild);
}
}
3.总代码
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define INCREMENT 10
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
typedef struct {
BiTree* base;
int front;
int rear;
int QueueSize;
}Queue;
void InitQueue(Queue& Q)
{
Q.base = (BiTree*)malloc(sizeof(BiTree) * MAXSIZE);
Q.front = Q.rear = 0;
Q.QueueSize = MAXSIZE;
}
void EnQueue(Queue& Q, BiTree e)
{
if ((Q.rear + 1) % MAXSIZE == Q.front)return;
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % MAXSIZE;
}
void DeQueue(Queue& Q, BiTree& e)
{
if (Q.front == Q.rear)return;
e = Q.base[Q.front];
Q.front = (Q.front + 1) % MAXSIZE;
}
//创建树
void CreateTree(BiTree& Root)
{
TElemType c;
if ((c = getchar()) != '\n')
{
Root = (BiTree)malloc(sizeof(BiTNode));
Root->data = c;
CreateTree(Root->lchild);
CreateTree(Root->rchild);
}
else Root = NULL;
}
//层序遍历
void RankTraverse(BiTree Root)
{
Queue Q;
InitQueue(Q);
EnQueue(Q, Root);
while (!(Q.rear == Q.front))
{
DeQueue(Q, Root);
putchar(Root->data);
if(Root->lchild)EnQueue(Q, Root->lchild);
if(Root->rchild)EnQueue(Q, Root->rchild);
}
}
int main()
{
BiTree Root;
CreateTree(Root);
printf("\nrank:");
RankTraverse(Root);
return 0;
}