这里的存储结构采用了二叉链表
1.定义存储结构
#include<stdio.h>
#include<stdlib.h>
typedef char eletype;
#define Max 16//这里假设二叉树只有4层
typedef struct node{
eletype data;
struct node * leftChild,*rightChild;
} BiTreeNode;//二叉节点
2.二叉树的创建函数
BiTreeNode* Create(){//创建一棵二叉树,返回根结点地址
printf("先添加虚结点,变成一棵完全二叉树\n");
printf("\n");
printf("按完全二叉树层序编号次序,依次输入每个结点名称,其中虚结点用#表示,结束用@表示\n");
BiTreeNode * r =NULL,*s,*Q[Max];
eletype ch;
int front=1,rear=0;
scanf(" %c",&ch);
while(ch!='@')
{
s=NULL;
if(ch!='#')//只要没有遇到虚结点,就为s动态开辟空间
{
s=( BiTreeNode*)malloc(sizeof(BiTreeNode));
s->data=ch;
s->rightChild=s->leftChild=NULL;
}
//将s结点入队
Q[++rear]=s;
if(rear==1)
r=s;//此时s就是根结点
else
{
if(s)
{
//给s结点找双亲结点
if(rear%2==0)//如果rear是偶数
Q[front]->leftChild=s;//那么它此时就是前一个结点的左孩子结点
else//如果是奇数
Q[front] ->rightChild=s;//那么它此时是前一个结点的右孩子结点
}
if(rear%2==1)//说明它此时是前一个结点的右孩子结点 ,
//也就是说前一个结点的双亲结点全部找到,该下一个了,所以接着front++
front++;
}
scanf(" %c",&ch);
}
return r;
}
这里在创建二叉树的时候还用到了队列,创建了一个指针类型的一维数组,用它来存放二叉节点,
由于我们的存储结构时二叉链表,所以最后只需返回一个根节点即可。
这里特别注意一下代码里scanf(" %c",&ch) 和原本scanf("%c",&ch)的区别。没错,前者在%c之前多了一个空格字符。
二者的区别是:scanf(" %c",&ch) 这里的空格表示scanf
在读取字符之前会忽略掉任何空白字符,包括空格、制表符(tab)和换行符。这意味着如果输入缓冲区中存在这些空白字符,scanf
会跳过它们,直到读取到下一个非空白字符。这对于读取以空格分隔的字符序列特别有用。
scanf("%c",&ch):没有空格的格式字符串告诉scanf
读取下一个可用的字符,不管它是不是空白字符。这意味着即使输入缓冲区中存在空白字符,scanf
也会读取它作为输入。
3.接下来是三种遍历
①前根遍历
void preSearch(BiTreeNode * r){
if (r){
printf(" %c",r->data);
preSearch(r->leftChild);//先根遍历左子树
preSearch(r->rightChild); //先根遍历右子树
}
}
遍历顺序:根节点 -> 左子树 -> 右子树
过程:
- 如果节点
r
不为空,首先打印节点r
的数据。 - 然后递归地对节点
r
的左子树进行先序遍历。 - 最后递归地对节点
r
的右子树进行先序遍历。
特点:先序遍历会首先访问根节点,然后是左子树上的所有节点,最后是右子树上的所有节点。
②后根遍历
void postSearch(BiTreeNode * r){
if (r){
postSearch(r->leftChild);//后根遍历左子树
postSearch(r->rightChild); //后根遍历右子树
printf(" %c",r->data);
}
}
遍历顺序:左子树 -> 右子树 -> 根节点
过程:
- 如果节点
r
不为空,首先递归地对节点r
的左子树进行后序遍历。 - 然后递归地对节点
r
的右子树进行后序遍历。 - 最后打印节点
r
的数据。
特点:后序遍历会访问左子树上的所有节点,然后是右子树上的所有节点,最后访问根节点。
这里每次递归都会先判断当前结点是否为空,如果为空,意味着左结点或者右节点到底了,会返回上一层函数的第二个postSearch函数。
③中根遍历
void inSearch(BiTreeNode * r){
if (r){
inSearch(r->leftChild);//中根遍历左子树
printf(" %c",r->data);
inSearch(r->rightChild); //中根遍历右子树
}
遍历顺序:左子树 -> 根节点 -> 右子树
过程:
- 如果节点
r
不为空,首先递归地对节点r
的左子树进行中序遍历。 - 然后打印节点
r
的数据。 - 最后递归地对节点
r
的右子树进行中序遍历。
特点:中序遍历会首先访问左子树上的所有节点,然后是根节点,最后是右子树上的所有节点
以及不太好理解的
④层序遍历
void levelorder(BiTreeNode * r){//层序遍历
BiTreeNode *Q[Max],*s;
int front=-1,rear=-1;//初始队列为空
if(!r) return ;//如果根节点为空则不进行任何操作
Q[++rear]=r;//将根结点存放在队列的第一个位置
while(front!=rear){//只要队列不满
s=Q[++front];//将队列前端的结点出队,此时的front=0,所以第一个出队的时根结点
printf(" %c",s->data);
if(s->leftChild)//如果根结点的左结点存在,则入队
Q[++rear]=s->leftChild;
if(s->rightChild)//如果根结点的右结点存在,则入队
Q[++rear]=s->rightChild ;
}
}
4.销毁函数
void Destory(BiTreeNode * r){//销毁二叉树r
if(r){
Destory(r->leftChild);
Destory(r->rightChild);
free(r);
}
}
5.主函数调用
int main(){
BiTreeNode * root=Create();
printf("先根遍历序列:\n");
preSearch(root);
printf("\n");
printf("后根遍历序列:\n");
postSearch(root);
printf("\n");
printf("中根遍历序列:\n");
inSearch(root);
printf("\n");
printf("层序遍历序列:\n");
levelorder(root);
printf("\n");
Destory(root) ;
return 0;
}
6.代码实现结果
为了更好的理解,我们展示一下二叉树图