二叉树的基本算法及遍历(递归)
创建二叉树(前序遍历)A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))
我们先创建一个 St 数组,以栈的形式对读取的字符(用来表示开始处理左子树的左括号,用来表示开始处理右子树的逗号,用来表示子树处理完毕的右括号)进行压栈和退栈。数组标号为当前根结点的标号。我们需要定义一个字符型变量对获取的字符串进行读取,定义一个整型变量 j = 0 进行数组下标变换,方便操作,以ch = str[0] 读取第一个字符,指针变量 p 用于操作新结点。
(二叉树的根结点)接下来我们进入while循环,循环弹出的条件是读取到字符串末尾的空字符,用switch函数检查我们读取的字符是用来处理的字符还是作为二叉树的结点数据的字符。此时 b 为空,为 p 动态分配内存,将ch的值赋予 p 的 data 域,将 p 赋予 b(根结点),将 j+1 ,ch置为 str[j],读取下一个结点。
(开始处理左右子树)接下来我们读取到第二个结点(物理序号为1),此时 ch 为左括号,将栈顶指针 top+1(top此时等于0),并将 p 赋予 St[top] ,此时 St[top] 为根结点,我们开始创建他的左子树,下一个读取到的字符如果是二叉树的结点数据,将其作为该结点的左孩子进行存储(St[top]->lchild = p);如果读取到的是逗号,则将 k 置为2,意为开始处理右子树,下一个读取到的字符将其作为该结点的右孩子进行存储(St[top]->rchild = p);如果读取到的是右括号,意为当前结点的子树处理完毕,将栈顶指针 top-1,使对应的左括号退栈,并继续读取下一个字符。
代码如下:
void CreateBTree(BTNode*& b, char* str) { //创建二叉树
BTNode* St[MaxSize], * p = NULL; //St用于保存双亲结点的栈
int top = -1, k, j = 0; //栈顶指针置为-1,k==1,p是栈顶结点的左孩子,k==2,p是右孩子
char ch; //用于传递字符
b = NULL; //初始化二叉树为空
ch = str[j]; //获取第一个字符
while (ch != '\0') { //str未扫描完时循环
switch (ch) {
case'(': //读取到(,意味着存在左子树,开始处理左子树
top++;
St[top] = p; //将上一个结点作为根结点,进行该结点左子树的创建
k = 1; //左子树
break; //读取下一个字符,该字符为该结点的左孩子
case')': //子树处理完毕
top--;
break;
case',': //开始处理右子树,此时St[top]是需要创建右子树的根结点
k = 2; //标记为处理右子树
break;
default: //二叉树的元素
p = (BTNode*)malloc(sizeof(BTNode)); //新建结点,直到下一次新建结点之前,p都为当前根结点
p->data = ch;
p->lchild = p->rchild = NULL; //处理该结点时,初始时其左右子树为空
if (b == NULL) { //若b未空,p置为二叉树的根结点
b = p; //根结点的首地址赋予b
}
else { //已建立二叉树根结点
switch (k) {
case 1:
St[top]->lchild = p;
break;
case 2:
St[top]->rchild = p;
break;
}
}
}
j++; //读取下一个元素
ch = str[j];
}
}
递归销毁二叉树
销毁二叉树时,我们采用自下而上的方法,使用后序遍历对二叉树进行销毁,检查根结点是否为空,然后销毁左子树,再销毁右子树,最后销毁根结点。
代码如下:
void DestroyBTree(BTNode*& b) { //递归销毁二叉树
if (b != NULL) { //如果二叉树不为空
DestroyBTree(b->lchild); //递归销毁左子树
DestroyBTree(b->rchild); //递归销毁右子树
free(b); //释放该结点
}
}
查找值为x的结点(前序遍历)
定义一个指针 p 用于后续操作。
判断二叉树是否为空树(b == NULL),如果是空树直接返回NULL。
如果不是空树:
①根结点的值就是要查找的x,直接返回b(根结点的地址)。
②如果根结点的值不是要查找的x,采用递归查找左子树,并将返回的地址赋予指针 p 。对 p 进行判断,如果 p 不为空,即在左子树递归查找时找到了值为 x 的结点,此时直接返回 p 。若 p 为空,则左子树中没有查找到 x ,此时查找右子树,并直接 return 递归返回的 p 。
代码如下:
BTNode* FindNode(BTNode* b, ElemType x) { //查找值为x的结点(前序遍历)
BTNode* p;
if (b == NULL) { //空树
return NULL;
}
else if (b->data == x) { //根结点的值就是x
return b;
}
else {
p = FindNode(b->lchild, x); //查找左子树
if (p != NULL) { //查找到值为x的结点
return p;
}
else { //查找右子树
return FindNode(b->rchild, x);
}
}
}
求某一结点的左右孩子
代码如下:
BTNode* LchildNode(BTNode* p) { //返回p结点的左孩子结点指针
return p->lchild;
}
BTNode* RchildNode(BTNode* p) { //返回p结点的右孩子结点指针
return p->rchild;
}
求二叉树的高度
定义两个整型变量 lchildh 和 rchildh 用于求左右子树高度。
首先判断二叉树是否为空树,空树直接返回0。
如果不是空树,递归左子树求左子树高度,再递归右子树求右子树高度,最后使用三目运算符返回数值较大的高度,返回值为 lchildh+1 或 rchildh+1 (自下而上累加)。
代码如下:
int BTHeight(BTNode* b) { //求二叉树b的高度
int lchildh, rchildh;
if (b == NULL) { //如果二叉树是空树
return 0;
}
else {
lchildh = BTHeight(b->lchild); //求左子树高度为lchildh
rchildh = BTHeight(b->rchild); //求右子树高度为rchildh
return (lchildh > rchildh) ? (lchildh + 1) : (rchildh + 1);
}
}
以括号表示法输出二叉树(前序遍历)
首先判断二叉树是否为空树(输出二叉树的条件)。
如果二叉树不为空,输出根结点 b ,接下来检查根结点 b 是否有左右子树,如果有,输出左括号,开始递归处理左子树,处理完左子树后,判断根结点 b 是否有右子树,如果有,先输出逗号,再递归处理右子树并输出右括号(两个操作在判断语句外)。
代码如下:
void DispBTree(BTNode* b) { //以括号表示法输出二叉树
if (b != NULL) { //根结点不为空
printf("%c", b->data); //输出根结点
if (b->lchild != NULL || b->rchild != NULL) { //该结点有孩子结点
printf("("); //有孩子结点才输出(
DispBTree(b->lchild); //判断左孩子(作为根结点),递归处理左子树
if (b->rchild != NULL) { //判断右孩子(作为根结点)
printf(","); //有右孩子才输出,
}
DispBTree(b->rchild); //递归处理右子树
printf(")"); //有孩子结点才输出)
}
}
}
递归求二叉树结点个数(中序遍历)
首先检查根结点是否为空,为空直接返回空。
如果不为空,开始处理根结点的左子树,直到左孩子为空时退出递归,使计数器+1,接着递归处理根结点的右子树。
代码如下:
void Count(BTNode* b) { //求二叉树的结点个数
if (b == NULL) {
return;
}
else {
Count(b->lchild);
count++;
Count(b->rchild);
}
}
前序遍历打印二叉树中的叶子结点
首先判断根结点是否为空,为空直接返回空。
如果不为空,检查该结点是否有左右孩子,如果没有,该结点为其中一个叶子结点,直接输出该结点的值;如果有左孩子或右孩子,开始递归处理左孩子(将其视为根结点递归遍历左子树),之后递归处理右孩子。
代码如下:
void PreOrder(BTNode* b) { //前序遍历打印二叉树中的叶子结点
if (b == NULL) {
return;
}
else {
if (b->lchild == NULL && b->rchild == NULL) { //该结点没有孩子结点
printf("%c", b->data);
}
PreOrder(b->lchild); //检查左孩子
PreOrder(b->rchild); //检查右孩子
}
}
前序遍历
判断根结点是否为空,如果为空结束函数运行,如果不为空,输出根结点,开始递归处理根结点的左孩子(处理左子树),再递归处理右孩子。
代码:
void PreOrder(BTNode* b) { //前序遍历
if (b != NULL) {
printf("%c", b->data);
PreOrder(b->lchild);
PreOrder(b->rchild);
}
}
中序遍历
根结点判空,如不为空,开始递归处理左孩子,处理完左子树后,输出根结点,再递归处理右孩子。
代码如下:
void InOrder(BTNode* b) { //中序遍历
if (b != NULL) {
InOrder(b->lchild);
printf("%c", b->data);
InOrder(b->rchild);
}
}
后序遍历
根结点判空,如不为空,开始递归处理左孩子,再递归处理右孩子,最后输出根结点。
代码如下:
void PostOrder(BTNode* b) { //后序遍历
if (b != NULL) {
PostOrder(b->lchild);
PostOrder(b->rchild);
printf("%c", b->data);
}
}
层序遍历
访问完根结点后,遍历左子树前,要将右子树压入栈
void TravLevel(BTNode* b) { //层序遍历
BTNode* Qu[MaxSize]; //定义环形队列
int front, rear; //定义队首和队尾指针
front = rear = 0; //置队列为空
if (b != NULL) {
printf("%c", b->data); //输出根结点
}
rear++; //根结点入队
Qu[rear] = b; //根结点入队
while (rear != front) { //相等时说明队空,二叉树已经遍历完成
front = (front + 1) % MaxSize; //队头指针循环增1(用于执行出队操作)
b = Qu[front]; //出队结点b
if (b->lchild != NULL) { //输出左孩子,并入队
printf("%c", b->lchild->data); //输出左孩子
rear = (rear + 1) % MaxSize; //队尾指针循环增1(用于执行入队操作)
Qu[rear] = b->lchild; //结点入队
}
if (b->rchild != NULL) { //输出右孩子,并入队
printf("%c", b->rchild->data); //输出右孩子
rear = (rear + 1) % MaxSize; //队尾指针循环增1(用于执行入队操作)
Qu[rear] = b->rchild; //结点入队
}
}
printf("\n");
}
主程序
二叉树基本算法
#include"btree.h"
int main(){
BTNode* b, * p, * lp, * rp, * child;
printf("1.创建二叉树\n");
CreateBTree(b, "A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))");
printf("2.输出二叉树:");
DispBTree(b);
printf("\n");
printf("3.输出'H'结点的左右孩子结点值:");
p = FindNode(b, 'H');
if (p != NULL) {
lp = LchildNode(p);
if (lp != NULL) {
printf("左:%c ", lp->data);
}
else {
printf("无左孩子 ");
}
rp = RchildNode(p);
if (rp != NULL) {
printf("右:%c", rp->data);
}
else {
printf("无右孩子");
}
}
printf("\n");
printf("4.输出二叉树b的高度:%d\n", BTHeight(b));
Count(b);
printf("5.输出二叉树b的结点个数:%d\n", count);
printf("6.按前序次序打印二叉树中的叶子结点:");
PreOrder(b);
printf("\n");
printf(".释放二叉树\n");
DestroyBTree(b);
return 0;
}
二叉树遍历
#include"btreealgorithm.h"
int main() {
BTNode* b;
printf("1.创建二叉树\n");
CreateBTree(b, "A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))");
printf("2.前序遍历输出二叉树:");
PreOrder(b);
printf("\n");
printf("3.中序遍历输出二叉树:");
InOrder(b);
printf("\n");
printf("4.后序遍历输出二叉树:");
PostOrder(b);
printf("\n");
printf("5.层序遍历输出二叉树:");
TravLevel(b);
printf("6.释放二叉树\n");
DestroyBTree(b);
return 0;
}