树-02:二叉树的建立及四种遍历方式
整理了完全二叉树的建立方法以及四种遍历方式(多种方法实现)
在理解本章后,可查看:
C数据结构与算法-基础整理-树-03:图解-通过前序遍历对递归执行原理的深入理解
C数据结构与算法-基础整理-树-04:图解-树的中序遍历
C数据结构与算法-基础整理-树-05:图解-树的后序遍历
在保证结构定义一样的前提下,代码可通用。
0x01.二叉树的结构定义:
typedef struct TreeNode
{
char data;
struct TreeNode* Left;//左孩子指针
struct TreeNode* Right;//右孩子指针
}TreeNode,*BinTree;//定义了这个结构体为 TreeNode ,这个二叉树指针为 BinTree
0x02.二叉树的建立:
简单一点:直接输入一个数据创建一个结点
具体注释见下一个。
void CtreateBiTree(BinTree *BT)
{
char ch;
scanf("%c",&ch);
if(ch=='#')
{
*BT=NULL;
}
else
{
*BT=(BinTree)malloc(sizeof(TreeNode));
if(!BT)
exit(-1);
(*BT)->data=ch;
CreateBiTree(&(*BT)->Left);
CreateBiTree(&(*BT)->Right);
}
}
复杂一点:引用字符串来创建二叉树
//二叉树的建立:递归实现,先序遍历的创建方法
//注意此处BT为指针的指针,因为没有返回值,所以要把二叉树的指针完全改变掉,需要指针的指针
//str为传入的字符串,该字符串用于创建二叉树的数据域
//n为当前创建节点数据域时使用到str的位数
//返回值为记录二叉树创建完毕后,使用了多少字符
//一定需要输入合法的用于创建树的字符串数据,不然会出错
int CreateBiTTree(BinTree *BT,char *str,int n)
{
printf("创建二叉树第 %d 个节点 ", n);
char ch = str[n];//每次使用一个字符赋值数据域
printf("%c\n", ch);//输出该数据
n++;
if (ch != '\0')//字符串没完就一直创建
{
if (ch == '#')//如果遇到字符 # ,说明下方没有了,是叶节点
{
*BT = NULL;//代表二叉树指针为空
}
else
{
*BT = (BinTree)malloc(sizeof(TreeNode));//*BT代表二叉树指针的值,BinTree表示这是一个指针,大小为一个二叉树结构体
if (!*BT)
{
exit(-1);//若分配空间失败,异常退出
}
(*BT)->data = ch;
n=CreateTree(&(*BT)->Left,str, n);//取指针的地址给此函数的第一个参数,因为此参数为二级指针
n=CreateTree(&(*BT)->Right, str, n);
}
}
return n;
}
0x03.二叉树的遍历
前序遍历:
遍历顺序说明:
先遍历所有的左子树,然后再遍历右子树。
用递归实现思路:
每遇到一个结点,先打印信息,再依次向左递归,向右递归。
用栈实现的思路:
用栈保存自己和自己的左孩子已经访问过的结点,然后出栈继续访问右孩子。
每遇到一个结点,先入栈,然后打印信息,然后去遍历它的左孩子,等左孩子遍历完成返回后,出栈,访问右孩子。
遍历顺序图解:
递归实现:
//前序遍历,递归实现
void PreorderTraversal(BinTree BT)
{
if (BT == NULL) return;
printf(" %c", BT->data);
PreorderTraversal(BT->Left);
PreorderTraversal(BT->Right);
return;
}
栈实现:
//前序遍历,栈实
//栈中元素分析:栈中保留的元素是自己和自己的左孩子已经访问过来,但右孩子还没访问的结点
void PreorderTraversal1(BinTree BT)
{
if (BT == NULL) return;
BinTree Stack[100];//存储结点的栈
int top = -1;
BinTree p = BT;//p指针用于遍历
while (p || top != -1)//退出循环条件为BT不存在且栈为空
{
if (p)
{
Stack[++top] = p;//如果BT存在,那么BT入栈
printf(" %c", p->data);//打印信息
p = p->Left;//继续访问左孩子
}
else
{
p = Stack[top--];//若访问到空,则拿出栈的一个元素,此元素的自己和左孩子已遍历,现在开始遍历右孩子
p = p->Right;
}
}
}
中序遍历:
遍历顺序说明:
从根结点开始,一直到最左端的叶结点,开始访问该结点,然后再访问父亲,当父亲的所有左孩子访问完,再访问右孩子。
用递归实现的思路:
一直向左找到最左端的结点,等所有左端结点打印完,再去访问右孩子。
用栈实现的思路:
用栈保存自己和自己的右孩子都没有访问过的结点。
每遇到一个结点,先入栈,然后向左遍历,当左边没有了,再打印该节点信息,再向右遍历,反复循环,一直到所有元素访问完成。
遍历顺序图解:
递归实现:
//中序遍历,递归实现
void InorderTraversal(BinTree BT)
{
if (BT == NULL) return;
InorderTraversal(BT->Left);
printf(" %c", BT->data);
InorderTraversal(BT->Right);
return;
}
//中序遍历,栈实现
//栈中元素分析:栈中保留的元素是自己和自己的右孩子都没访问过的结点
void InorderTraversal(BinTree BT)
{
if (BT == NULL) return;
BinTree Stack[100];
int top = -1;
BinTree p = BT;//p指针用于遍历
while (p || top != -1)//退出循环条件为BT不存在且栈为空
{
if (p)
{
Stack[++top] = p;//如果BT存在,那么BT入栈
p = p->Left;//继续访问左孩子
}
else
{
p = Stack[top--];//若访问到空,则拿出栈的一个元素,此元素的左孩子已遍历,现在开始遍历右孩子
printf(" %c", p->data);//打印信息
p = p->Right;
}
}
}
后序遍历:
遍历顺序说明:
从左到右,先叶子,后结点。
用递归实现思路:
将左孩子,右孩子全部递归完成再打印具体信息。
用栈实现思路:
设置一个标志栈,用于保存结点栈的标志量,是第几次返回。
每遇到一个结点,先向左去找,置标志量为1,然后从左孩子遍历完退回来之后,置标志量为2,然后去遍历右孩子,从右孩子返回后,打印栈中结点信息,再将该结点出栈。
遍历顺序图解:
递归实现:
//后序遍历,递归实现
void PostorderTraversal(BinTree BT)
{
if (BT == NULL) return;
PostorderTraversal(BT->Left);
PostorderTraversal(BT->Right);
printf(" %c", BT->data);
return;
}
栈实现:
//后序遍历,栈实现
//栈中元素分析:右孩子和自身都没有访问过的元素,第一次访问,继续访问左孩子,第二次访问,继续向右孩子访问,等左右孩子都访问完了,访问自身
void PostorderTraversal1(BinTree BT)
{
if (BT == NULL) return;
BinTree Stack[100];
int top = -1;
int flagStack[100];//此栈用于记录访问结点的次数,访问一次记1,访问两次记2,第三次访问输出结点值。
BinTree p = BT;
while (p || top != -1)
{
if (p)//第一次访问结点,flag=1
{
Stack[++top] = p;
flagStack[top] = 1;
p = p->Left;
}
else
{
if (flagStack[top] == 1)//第二次访问,说明从左孩子退回,继续去访问右孩子,取出栈中元素,但不出栈
{
p = Stack[top];
flagStack[top] = 2;
p = p->Right;
}
else//不等于1,那么一定是第三次访问,从右孩子退回,该输出自己的元素值了。
{
p = Stack[top--];
printf(" %c", p->data);//打印信息
p = NULL;//说明此结点的左右孩子均已完成遍历,那么将p置空,继续回退。
}
}
}
}
层序遍历–队列实现
遍历顺序说明:
从根结点开始,一层一层,从左到右访问。
队列实现思路:
每遇到一个元素先打印信息,然后入队,若左孩子存在,则左孩子入对,右孩子存在,则右孩子后入队,等队中没有元素,就是全部遍历完成。
遍历顺序说明:
//层序遍历,队列实现
//队中元素分析:自身访问过,左右孩子未访问过
void LevelorderTraversal(BinTree BT)
{
BinTree Queue[100];//创建一个二叉树指针类型的队列
int head = -1;
int tail = -1;
if (BT)
{
Queue[++tail] = BT;//如果头结点非空,头结点入队
}
while (head < tail)//队列为空是遍历完成的条件
{
BinTree tmp = Queue[++head];//队头出队,打印信息
printf("%c -> ", tmp->data);
if (tmp->Left)//如果左孩子存在,左孩子先入队
{
Queue[++tail] = tmp->Left;
}
if (tmp->Right)
{
Queue[++tail] = tmp->Right;
}
}
}
0x04.写主函数测试
#include<stdio.h>
#include<stdlib.h>
int main()
{
BinTree BT;//注意BT为指针
int k;//记录创建函数的返回值
char str[100];
printf("\n\n**********\t请输入一串字符用于创建二叉树:");
gets_s(str);
k = CreateTree(&BT, str, 0);
printf("\n\n**********\t二叉树创建成功!!!共使用了 %d 个字符",k);
printf("\n\n**********\t开始该二叉树的前序遍历......");
PreorderTraversal(BT);
printf("\n\n**********\t开始该二叉树的中序遍历......");
InorderTraversal(BT);
printf("\n\n**********\t开始该二叉树的后序遍历......");
PostorderTraversal(BT);
printf("\n\n**********\t开始该二叉树的层序遍历......");
LevelorderTraversal(BT);
return 0;
}
测试数据:AB#D##C##
测试截图: