C语言数据结构之二叉树的层次建树及遍历方法(前序,中序,后序,层次遍历)
tips:前些天学习了C语言数据结构链表,栈,队列。今天来学习一下C语言数据结构之二叉树的各种操作。
注意:二叉树的层次建树是建立的完全二叉树。
完全二叉树:对满二叉树进行编号,约定编号从1开始,从上到下,从左至右,每个结点的编号与其深度相同的满二叉树结点的编号一一对应。
完全二叉树的性质:
-
具有n个结点的完全二叉树的深度为 ⌊ ⌋+1;
-
若编号为i的结点左右子树存在,则其左子树的编号为2i,右子树编号为2i+1;
-
编号从1开始,长度为n的完全二叉树最后一个父结点编号为⌊n/2⌋;
编号从0开始,长度为n的完全二叉树最后一个父结点编号为⌊n/2⌋-1;
一、二叉树的层次建树(完全二叉树)
二叉树结构体:
//二叉树结构体
#define N 11 //0不存放数值,这里存10个元素,长度为11
typedef struct node
{
char c;//结点的数据,这里为了方便定义char
struct node *left;//左指针
struct node *right;//右指针
}Node,*pNode;
层次遍历需要用到的队列:
typedef struct LinkList
{
pNode p;//将树的结点作为队列的值域
struct LinkList *pNext;
}LinkList,*pLinkList;
//队列
typedef struct Queue
{
pLinkList front, rear;//队头队尾指针
}Queue,*pQueue;
队列的操作前面已经学习过,这里就不展开了(注意:这里的值域是个结点)。
这里需要用到队列操作:
//初始化队列
void InitQueue(pQueue que);
//入队
void EnQueue(pQueue que, pNode p);
//出队
void DeQueue(pQueue que);
//访问队头元素
pNode getFront(pQueue que);
//判断队列是否为空
int QueueEmpty(pQueue que);
1、二叉树的初始化
思路:
- 将树的每个结点循环初始化;
- 将树结点的值域初始化对应的值;
- 将树指针域初始化为空;
具体实现:
//树的初始化
void InitBiTree(pNode *p,char *c)//c[]存放数据
{
int i;
//对树的结点初始化,0号结点也要初始化,可以不用它
for (i = 0; i < N; i++)
{
p[i] = (pNode)malloc(sizeof(Node));
p[i]->c = c[i];//结点的值域初始化
p[i]->left = NULL;
p[i]->right = NULL;
}
}
2、二叉树的层次建树
思路:
- 将编号为1的树结点作为根结点;
- 循环,为将要进树的除根结点以外的结点寻找合适位置;
- 若结点左子树为空,则插入左子树;
- 若结点左子树非空,右子树为空,则插入右子树;
- 若结点左右子树均不为空,说明此结点已满,寻找下个结点;
具体实现:
//层次建树,参数是树的结点
void CreateBitree(pNode *p)
{
int i, j;
//开始层次建树,编号为1的结点作为根结点,找位置插入树的结点编号从2开始
for (i = 2; i < N; i++)//进树的元素
{
for (j = 1; j < N; j++)//为进树的元素找合适的位置插入
{
if (p[j]->left == NULL)
{
p[j]->left = p[i];//左子树为空,插入左子树
break;
}
if (p[j]->right == NULL)
{
p[j]->right = p[i];//左子树为空,插入左子树
break;
}
}
}
}
实现效果:
二、二叉树的遍历
1、二叉树前序遍历(根左右)
思路:
- 递归实现遍历;
- 先访问根结点,再访问左子树,再访问右子树
具体实现:
//树的前序遍历(根左右)
void preorder(pNode tree)//传根结点作为参数
{
//判断树是否为空
if (tree != NULL)
{
printf("%c", tree->c);
preorder(tree->left);
preorder(tree->right);
}
}
2、二叉树中序遍历(左根右)
思路:
- 递归实现遍历;
- 先访问左子树,再访问根结点,再访问右子树;
具体实现:
//树的中序遍历(左根右)
void midorder(pNode tree)
{
if (tree != NULL)
{
midorder(tree->left);
printf("%c", tree->c);
midorder(tree->right);
}
}
3、二叉树后序遍历(左右根)
思路:
- 递归实现遍历;
- 先访问左子树,再访问右子树,再访根结点;
具体实现:
//树的后序遍历(左右根)
void lastorder(pNode tree)
{
if (tree != NULL)
{
lastorder(tree->left);
lastorder(tree->right);
printf("%c", tree->c);
}
}
4、二叉树层次遍历(借助队列)
思路:
- 初始时,将根结点入队,并访问根结点;
- 若有左子树,则将左子树的根结点入队;
- 若有右子树,则将右子树的根结点入队;
- 将根结点出队;
- 重复上述步骤,直到队列为空;
具体实现:
//树的层次遍历
void levelorder(pNode tree)
{
Queue queue;//定义一个队列
pQueue que = &queue;
InitQueue(que);
EnQueue(que,tree);//根结点入队
while (QueueEmpty(que)!= 1)//不为空
{
printf("%c",getFront(que)->c);//访问输出队首结点
//当队首结点左右子树存在时,将其左右子树结点入队
if (getFront(que)->left!=NULL)
{
EnQueue(que, getFront(que)->left);//左子树入队
}
if (getFront(que)->right != NULL)
{
EnQueue(que, getFront(que)->right);//右子树入队
}
DeQueue(que);//队首元素出队
}
}
至此,完全二叉树的创建以及遍历过程均已实现,接下来在main()函数中测试一下:
int main()
{
//准备建树的元素,0号下标不存元素
char c[N] = { ' ','A','B','C','D','E','F','G','H','I','J' };
pNode p[N];//树的结点
pNode tree;//根结点
InitBiTree(p, c);//初始化树
CreateBitree(p);//建树
tree = p[1];//1号结点作为根结点
//前序打印树(根左右)
printf("前序遍历:");
preorder(tree);
printf("\n");
//中序打印树(左根右)
printf("中序遍历:");
midorder(tree);
printf("\n");
//后序打印树(左右根)
printf("后序遍历:");
lastorder(tree);
printf("\n");
//层次遍历树
printf("层次遍历:");
levelorder(tree);
printf("\n");
return 0;
}
测试结果:
tips:成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。