目录
前言
嗯,最近在搞二叉树,因为个人觉得代码要保留下来,都是放在电脑里面又不好找,放多了会比较乱,所以就将一些学习过程中写的代码记录下来发表了,当然,虽然主要目的是记录,但是博主还是会很认真的将代码解释清楚的,毕竟不能误人子弟嘛。嗯。。。。因为博主想把创建二叉树的方法还有遍历方法都写下来,所以代码会比较长,于是博主就将代码分成几个文件来写了。
如题嘛,这篇博客是介绍二叉树的创建与遍历的,还没有开始二叉树的插入、删除、查找这些哦,后面博主有时间也会写出来的,不过想看插入删除这些的也不用急着退出去哦,看看博主的代码或许也会有新的领悟的(实在是自夸了)。
代码与代码解释
还有就是,因为写的是字符二叉树,所以博主就写了两种创建二叉树的方法:中序创建和层序创建。中序创建比较简单,是用递归实现的。
下面就开始上代码了,必要的部分博主会讲解,比较简单的看一下注释就好了
头文件BinTreeH.h代码
//(字符)二叉树结构体定义的头文件
#include<stdio.h>
#include<stdlib.h>
typedef char BTElementType;//定义二叉树的元素类型
typedef struct TNode {//定义二叉树的结构体
BTElementType Data;//树的数据域
struct TNode* Left;//树的左指针,左子树
struct TNode* Right;//树的右指针,右子树
bool Visit;//布尔类型的变量,在后面用于二叉树的非递归遍历
}*BinTree;
头文件StackToBT.h代码
这个头文件是定义堆栈的
//用来服务二叉树的堆栈的头文件,堆栈元素为结构体结点
//堆栈定义为后入先出
#include"BinTreeH.h"
typedef BinTree ElementType;//将堆栈元素定义为结构体结点
typedef struct SNode {//定义堆栈结构体
ElementType Data;
struct SNode* Next;
}*Stack;
Stack CreateStack() {//创建堆栈
Stack S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;//头节点空置
return S;
}
bool IsEmptyS(Stack S) {//判断堆栈是狗为空
return (S->Next == NULL);
}
void Push(Stack S,ElementType x) {//将堆栈元素入栈,头插法
Stack p = (Stack)malloc(sizeof(struct SNode));
p->Data = x;
p->Next = S->Next;
S->Next = p;
}
ElementType Pop(Stack S) {//将栈顶元素弹出,也就是将头节点的下一个结点弹出
if (S->Next == NULL) return NULL;
Stack p = S->Next;
ElementType x = p->Data;
S->Next = p->Next;
free(p);//释放空间
return x;
}
头文件QueueToBT.h代码
这个头文件是定义队列的
//用来服务二叉树实现的队列头文件,队列元素为结构体结点
//队列定义为先入先出
#include"StackToBT.h"
typedef BinTree ElementType;//将队列元素定义为结构体结点
typedef struct QNode {//定义队列结构体
ElementType Data;
struct QNode* Next;
}*Queue;
Queue CreateQueue() {
Queue Q = (Queue)malloc(sizeof(struct QNode));
Q->Next = NULL;//头节点空置
return Q;
}
bool IsEmpty(Queue Q) {//判断队列是否为空
return (Q->Next == NULL);
}
void AddQ(Queue Q, ElementType x) {//将元素入队,尾插法
Queue q = (Queue)malloc(sizeof(struct QNode));
q->Data = x;
q->Next = NULL;
if (IsEmpty(Q))Q->Next = q;//如果队列为空则直接将新的结点放到头节点后面
else {
Queue p = Q->Next;
while (p->Next != NULL)p = p->Next;
p->Next = q;
}
}
ElementType DeleteQ(Queue Q) {
if (IsEmpty(Q)) return NULL;//如果队列为空,直接返回空
Queue p = Q->Next;
ElementType x = p->Data;
Q->Next = p->Next;
free(p);
return x;//将元素返回
}
头文件BinTree.h代码
这个头文件才是大头,是拿来创建二叉树和遍历二叉树的
特别需要注意的是重载问题,博主用的编译软件是vs,如果导入自己写的头文件时,导入的头文件有重复的部分(也就是有的函数名一样这些问题),那么会报错“重载”,博主一开始也不知道问什么报错,被难了挺久,后来重新写才发现是这个错误(博主确实是菜鸡~)。
非递归实现二叉树的后序遍历解释
比较难的就是后序遍历二叉树的非递归算法实现了,这里解释一下:
先简单讲一下我的大概思路,在定义二叉树结构体的时候我就定义了一个布尔类型的变量Visit,现在就是要用这一个变量来实现后序遍历,主体思路和前面的先序、中序遍历是一样的
就是在将每个结点压栈之前,将这个结点的Visit值(也就是结构体里定义的布尔值)赋为false在将一个结点的左子树遍历完之后,又返回到了这个结点,则将Visit变为true,并将这个结点重新压栈
当将该节点的右子树也访问完之后,再次返回这个结点,这时候Visit为true,则直接进行输出。
也就是说,每个结点都会遍历两遍才进行输出
这里将非递归实现后序遍历的代码单独截出来配合文字看:
void PostPrint2(BinTree BT) {//后序遍历的非递归实现
Stack S = CreateStack();
BinTree T = BT;
while (T != NULL || !IsEmptyS(S)) {
while (T != NULL) {//
T->Visit = false;//将初入栈的结点的Visit值赋为false
Push(S, T);//入栈
T = T->Left;//往左移动
}
T = Pop(S);
if (T->Visit == false) {//Visit为false,则将结点重新压栈
T->Visit = true;
Push(S, T);
T = T->Right;//指向右子树(后面会进行)
}
else if (T->Visit == true) {//Visit为true,直接进行输出
printf("%c", T->Data);
T=NULL;//将T的元素输出后,将T赋为NULL,以跳过重新开始循环时找左节点的过程
}
}
}
下面是整个的头文件代码
#include"QueueToBT.h"
//导入队列头文件
//只需要导入一个队列的头文件,因为队列的头文件中已经导入了堆栈的头文件
//而堆栈的头文件已经导入了定义二叉树结构体的头文件
//不能导入有重复部分的头文件,不然会报错:某函数不能重载
BinTree CreateBT() {//层序创建一颗二叉树
//输入的字符为'0'表示为空结点
BTElementType Data;
scanf("%c", &Data);//输入根节点数据
if (Data == '0')return NULL;
BinTree BT = (BinTree)malloc(sizeof(struct TNode));
BT->Data = Data;
Queue Q = CreateQueue();//创建一个空队列
AddQ(Q,BT);//根节点入队
while (!IsEmpty(Q)) {//当队列不为空时继续循环
BinTree T = DeleteQ(Q);//出队
scanf("%c", &Data);//输入左子树数据
if (Data != '0') {
T->Left = (BinTree)malloc(sizeof(struct TNode));
T->Left->Data = Data;
AddQ(Q, T->Left);//将左子树入队
}
else T->Left = NULL;
scanf("%c", &Data);//输入右子树数据
if (Data != '0') {
T->Right = (BinTree)malloc(sizeof(struct TNode));
T->Right->Data = Data;
AddQ(Q, T->Right);//将右子树入队
}
else T->Right = NULL;
}
return BT;//最后返回根节点(树根)
}
BinTree CreateBinTree(BinTree BT) {//中序创建二叉树
BTElementType Data;
scanf("%c", &Data);
if (Data == '0')return NULL;
else {
BT = (BinTree)malloc(sizeof(struct TNode));
BT->Data = Data;
BT->Left=CreateBinTree(BT->Left);
BT->Right = CreateBinTree(BT->Right);
}
return BT;
}
//下面是树的各种遍历方式
void PrePrint(BinTree BT) {//递归实现二叉树的先序遍历
//先序:根->左子树->右子树
if (BT != NULL) {
printf("%c", BT->Data);
PrePrint(BT->Left);
PrePrint(BT->Right);
}
}
void InPrint(BinTree BT) {//递归实现中序遍历二叉树
//中序:左子树->根->右子树
if (BT != NULL) {
InPrint(BT->Left);
printf("%c", BT->Data);
InPrint(BT->Right);
}
}
void PostPrint(BinTree BT) {//递归实现二叉树的后序遍历
//后序:左子树->右子树->根
if (BT != NULL) {
PostPrint(BT->Left);
PostPrint(BT->Right);
printf("%c", BT->Data);
}
}
void PrePrint2(BinTree BT) {//非递归实现先序遍历二叉树
Stack S = CreateStack();//创建堆栈
BinTree T = BT;
while (T != NULL || !IsEmptyS(S)) {//当T不为NULL或堆栈不为空就继续进行循环
//注:有一个成立就可以继续循环
while (T != NULL) {//一直向左并将沿途结点压入堆栈
printf("%c", T->Data);//先进行输出(先序遍历)
Push(S,T);//压入堆栈
T = T->Left;//指向左子树
}
T = Pop(S);//栈顶元素出栈
T = T->Right;//转向右子树
}
}
void InPrint2(BinTree BT) {//非递归实现中序遍历二叉树
Stack S = CreateStack();
BinTree T = BT;
while(T!=NULL||!IsEmptyS(S)){
while (T != NULL) {
Push(S, T);
T = T->Left;
}
T = Pop(S);
printf("%c", T->Data);
T = T->Right;
}
}
void PostPrint2(BinTree BT) {//后序遍历的非递归实现
Stack S = CreateStack();
BinTree T = BT;
while (T != NULL || !IsEmptyS(S)) {
while (T != NULL) {//
T->Visit = false;//将初入栈的结点的Visit值赋为false
Push(S, T);//入栈
T = T->Left;//往左移动
}
T = Pop(S);
if (T->Visit == false) {//Visit为false,则将结点重新压栈
T->Visit = true;
Push(S, T);
T = T->Right;//指向右子树(后面会进行)
}
else if (T->Visit == true) {//Visit为true,直接进行输出
printf("%c", T->Data);
T=NULL;
}
}
}
void LevelPrint(BinTree BT) {//层序遍历二叉树
if (BT == NULL)return;
BinTree T = BT;
Queue Q = CreateQueue();//创建队列
AddQ(Q, T);//将根节点入队
while (!IsEmpty(Q)) {
T = DeleteQ(Q);
printf("%c", T->Data);
if (T->Left != NULL)AddQ(Q, T->Left);//将左子树入队
if (T->Right != NULL)AddQ(Q, T->Right);//将右子树入队
}
}
//这个是将二叉树叶节点输出的递归算法,非递归就不写了
void PrePrintLeaves(BinTree BT) {//二叉树叶节点的输出算法
if (BT != NULL) {
if (!BT->Left && BT->Right) {
printf("%c", BT->Data);
PrePrintLeaves(BT->Left);
PrePrintLeaves(BT->Right);
}
}
}
主函数代码
#include"BinTree.h"
int main() {
BinTree bt = CreateBT();
printf("递归实现先序遍历:\n");
PrePrint(bt);
printf("\n");
printf("递归实现中序遍历:\n");
InPrint(bt);
printf("\n");
printf("递归实现后序遍历:\n");
PostPrint(bt);
printf("\n");
printf("非递归实现先序遍历:\n");
PrePrint2(bt);
printf("\n");
printf("非递归实现中序遍历:\n");
InPrint2(bt);
printf("\n");
printf("非递归实现后序遍历:\n");
PostPrint2(bt);
printf("\n");
printf("最后是(非递归)层序的遍历:\n");
LevelPrint(bt);
printf("\n");
return 0;
}
总代码与运行结果
为了方便小伙伴们进行验证,博主专门将各个文件整合成了一个主函数文件,也有200+行了,所以求个赞赞吧!
总代码
#include<stdio.h>
#include<stdlib.h>
typedef char BTElementType;//定义二叉树的元素类型
typedef struct TNode {//定义二叉树的结构体
BTElementType Data;//树的数据域
struct TNode* Left;//树的左指针,左子树
struct TNode* Right;//树的右指针,右子树
bool Visit;//布尔类型的变量,在后面用于二叉树的非递归遍历
}*BinTree;
//---------堆栈部分
typedef BinTree ElementType;//将堆栈元素定义为结构体结点
typedef struct SNode {//定义堆栈结构体
ElementType Data;
struct SNode* Next;
}*Stack;
Stack CreateStack() {//创建堆栈
Stack S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;//头节点空置
return S;
}
bool IsEmptyS(Stack S) {//判断堆栈是狗为空
return (S->Next == NULL);
}
void Push(Stack S, ElementType x) {//将堆栈元素入栈,头插法
Stack p = (Stack)malloc(sizeof(struct SNode));
p->Data = x;
p->Next = S->Next;
S->Next = p;
}
ElementType Pop(Stack S) {//将栈顶元素弹出,也就是将头节点的下一个结点弹出
if (S->Next == NULL) return NULL;
Stack p = S->Next;
ElementType x = p->Data;
S->Next = p->Next;
free(p);//释放空间
return x;
}
//--------------队列部分
typedef struct QNode {//定义队列结构体
ElementType Data;
struct QNode* Next;
}*Queue;
Queue CreateQueue() {
Queue Q = (Queue)malloc(sizeof(struct QNode));
Q->Next = NULL;//头节点空置
return Q;
}
bool IsEmpty(Queue Q) {//判断队列是否为空
return (Q->Next == NULL);
}
void AddQ(Queue Q, ElementType x) {//将元素入队,尾插法
Queue q = (Queue)malloc(sizeof(struct QNode));
q->Data = x;
q->Next = NULL;
if (IsEmpty(Q))Q->Next = q;//如果队列为空则直接将新的结点放到头节点后面
else {
Queue p = Q->Next;
while (p->Next != NULL)p = p->Next;
p->Next = q;
}
}
ElementType DeleteQ(Queue Q) {
if (IsEmpty(Q)) return NULL;//如果队列为空,直接返回空
Queue p = Q->Next;
ElementType x = p->Data;
Q->Next = p->Next;
free(p);
return x;//将元素返回
}
//--------创建与遍历部分
BinTree CreateBT() {//层序创建一颗二叉树
//输入的字符为'0'表示为空结点
BTElementType Data;
scanf("%c", &Data);//输入根节点数据
if (Data == '0')return NULL;
BinTree BT = (BinTree)malloc(sizeof(struct TNode));
BT->Data = Data;
Queue Q = CreateQueue();//创建一个空队列
AddQ(Q, BT);//根节点入队
while (!IsEmpty(Q)) {//当队列不为空时继续循环
BinTree T = DeleteQ(Q);//出队
scanf("%c", &Data);//输入左子树数据
if (Data != '0') {
T->Left = (BinTree)malloc(sizeof(struct TNode));
T->Left->Data = Data;
AddQ(Q, T->Left);//将左子树入队
}
else T->Left = NULL;
scanf("%c", &Data);//输入右子树数据
if (Data != '0') {
T->Right = (BinTree)malloc(sizeof(struct TNode));
T->Right->Data = Data;
AddQ(Q, T->Right);//将右子树入队
}
else T->Right = NULL;
}
return BT;//最后返回根节点(树根)
}
BinTree CreateBinTree(BinTree BT) {//中序创建二叉树
BTElementType Data;
scanf("%c", &Data);
if (Data == '0')return NULL;
else {
BT = (BinTree)malloc(sizeof(struct TNode));
BT->Data = Data;
BT->Left = CreateBinTree(BT->Left);
BT->Right = CreateBinTree(BT->Right);
}
return BT;
}
//下面是树的各种遍历方式
void PrePrint(BinTree BT) {//递归实现二叉树的先序遍历
//先序:根->左子树->右子树
if (BT != NULL) {
printf("%c", BT->Data);
PrePrint(BT->Left);
PrePrint(BT->Right);
}
}
void InPrint(BinTree BT) {//递归实现中序遍历二叉树
//中序:左子树->根->右子树
if (BT != NULL) {
InPrint(BT->Left);
printf("%c", BT->Data);
InPrint(BT->Right);
}
}
void PostPrint(BinTree BT) {//递归实现二叉树的后序遍历
//后序:左子树->右子树->根
if (BT != NULL) {
PostPrint(BT->Left);
PostPrint(BT->Right);
printf("%c", BT->Data);
}
}
void PrePrint2(BinTree BT) {//非递归实现先序遍历二叉树
Stack S = CreateStack();//创建堆栈
BinTree T = BT;
while (T != NULL || !IsEmptyS(S)) {//当T不为NULL或堆栈不为空就继续进行循环
//注:有一个成立就可以继续循环
while (T != NULL) {//一直向左并将沿途结点压入堆栈
printf("%c", T->Data);//先进行输出(先序遍历)
Push(S, T);//压入堆栈
T = T->Left;//指向左子树
}
T = Pop(S);//栈顶元素出栈
T = T->Right;//转向右子树
}
}
void InPrint2(BinTree BT) {//非递归实现中序遍历二叉树
Stack S = CreateStack();
BinTree T = BT;
while (T != NULL || !IsEmptyS(S)) {
while (T != NULL) {
Push(S, T);
T = T->Left;
}
T = Pop(S);
printf("%c", T->Data);
T = T->Right;
}
}
void PostPrint2(BinTree BT) {//后序遍历的非递归实现
Stack S = CreateStack();
BinTree T = BT;
while (T != NULL || !IsEmptyS(S)) {
while (T != NULL) {//
T->Visit = false;//将初入栈的结点的Visit值赋为false
Push(S, T);//入栈
T = T->Left;//往左移动
}
T = Pop(S);
if (T->Visit == false) {//Visit为false,则将结点重新压栈
T->Visit = true;
Push(S, T);
T = T->Right;//指向右子树(后面会进行)
}
else if (T->Visit == true) {//Visit为true,直接进行输出
printf("%c", T->Data);
T=NULL;
}
}
}
void LevelPrint(BinTree BT) {//层序遍历二叉树
if (BT == NULL)return;
BinTree T = BT;
Queue Q = CreateQueue();//创建队列
AddQ(Q, T);//将根节点入队
while (!IsEmpty(Q)) {
T = DeleteQ(Q);
printf("%c", T->Data);
if (T->Left != NULL)AddQ(Q, T->Left);//将左子树入队
if (T->Right != NULL)AddQ(Q, T->Right);//将右子树入队
}
}
//这个是将二叉树叶节点输出的递归算法,非递归就不写了
void PrePrintLeaves(BinTree BT) {//二叉树叶节点的输出算法
if (BT != NULL) {
if (!BT->Left && BT->Right) {
printf("%c", BT->Data);
PrePrintLeaves(BT->Left);
PrePrintLeaves(BT->Right);
}
}
}
//-------主函数部分
int main() {
BinTree bt = CreateBT();
printf("递归实现先序遍历:\n");
PrePrint(bt);
printf("\n");
printf("递归实现中序遍历:\n");
InPrint(bt);
printf("\n");
printf("递归实现后序遍历:\n");
PostPrint(bt);
printf("\n");
printf("非递归实现先序遍历:\n");
PrePrint2(bt);
printf("\n");
printf("非递归实现中序遍历:\n");
InPrint2(bt);
printf("\n");
printf("非递归实现后序遍历:\n");
PostPrint2(bt);
printf("\n");
printf("最后是(非递归)层序的遍历:\n");
LevelPrint(bt);
printf("\n");
return 0;
}
运行结果(仅展示一个样例)
结语
呜呼~!花了一个下午终于将这篇博客写完了(主要是在写代码),如果小伙伴们发现代码有误或者其它问题,欢迎进行评论提问哦!博主看到会回复的。