二叉树是一种重要的数据结构,本篇包括二叉树的创建、三种二叉树遍历方式(递归与非递归)、计算二叉树结点个数、计算二叉树高度等操作。
一、存储结构
typedef char TElemType;
typedef struct BiTNode
{
TElemType data;
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode, *BiTree;
二、基本操作
1、先序创建二叉树
void CreateBiTree(BiTree &T)
{
TElemType ch;
scanf("%c", &ch);
if(ch == '\n')
return;
if(ch == ' ')
T = NULL;
else
{
T = (BiTree)malloc(sizeof(BiTNode));
if(!T)
return;
T->data = ch;//生产根节点
CreateBiTree(T->lchild);//构造左子树
CreateBiTree(T->rchild);//构造右子树
}
}
2、后序递归销毁二叉树
void DestoryBiTree(BiTree &T)
{
if(T == NULL)
return;
DestoryBiTree(T->lchild);
DestoryBiTree(T->rchild);
free(T);T = NULL;
}
3、二叉树遍历
(1)前序遍历(含非递归)
递归:
void PreOrderTraverse(BiTree T, void (*Visit)(TElemType e))
{
if(T == NULL)
return;
Visit(T->data);
PreOrderTraverse(T->lchild, Visit);
PreOrderTraverse(T->rchild, Visit);
}
/*
非递归先序遍历
在遍历某一个二叉(子)树时,以一当前指针记录当前要处理的二叉(左子)树,
以一个栈保存当前树之后处理的右子树。首先访问当前树的根结点数据,接下来
应该依次遍历其左子树和右子树,然而程序的控制流只能处理其一,所以考虑将
右子树的根保存在栈里面,当前指针则指向需先处理的左子树,为下次循环做准
备;若当前指针指向的树为空,说明当前树为空树,不需要做任何处理,直接弹
出栈顶的子树,为下次循环做准
*/
void PreOrderTraverse_1(BiTree T, void (*Visit)(TElemType e))
{
//定义栈
const int MAXSIZE = 100;
struct BiTNode *stack[MAXSIZE], *p;
int top = -1;
if(T == NULL)
return;
p = T;
while(p || top != -1)
{
if(p)
{
Visit(p->data);
stack[++top] = p->rchild;
p = p->lchild;
}
else
p = stack[top--];
}
}
(2)中序遍历(含非递归)
递归:
void InOrderTraverse(BiTree T)
{
if(T == NULL)
return;
InOrderTraverse(T->lchild);
printf("%c", T->data);
InOrderTraverse(T->rchild);
}
非递归:
/*
非递归中序遍历
若当前树不为空树,则访问其根结点之前应先访问其左子树,因而先将当前
根节点入栈,然后考虑其左子树,不断将非空的根节点入栈,直到左子树为一空树;
当左子树为空时,不需要做任何处理,弹出并访问栈顶结点,然后指向其右子树,为下次循环做准备
*/
void InOrderTraverse_1(BiTree T)
{
//定义栈
const int MAXSIZE = 100;
struct BiTNode *stack[MAXSIZE], *p;
int top= -1;//初始化栈
if (T == NULL)
return;
p = T;
while(p || top != -1)
{
if(p)//根指针进栈,遍历左子树
{
stack[++top] = p;
p = p->lchild;
}
else//根指针退栈,访问根节点,遍历右子树
{
p = stack[top--];
printf("%c", p->data);
p = p->rchild;
}
}
}
另一种非递归方式:
void InOrderTraverse_2(BiTree T)
{
//定义栈
const int MAXSIZE = 100;
struct BiTNode *stack[MAXSIZE], *p;
int top= -1;//初始化栈
if (T == NULL)
return;
stack[++top] = T;//根指针进栈
while(top != -1)//栈不空
{
while((p = stack[top]))//向左走到尽头
stack[++top] = p->lchild;
--top;//空指针退栈
if(top != -1)//访问结点,向右一步
{
p = stack[top--];
printf("%c",p->data);
stack[++top] = p->rchild;
}
}
}
(3)后序遍历(含非递归)
递归:
void PostOrderTraverse(BiTree T)
{
if (T == NULL)
return;
PostOrderTraverse(T->lchild);
PostOrderTraverse(T->rchild);
printf("%c", T->data);
}
非递归:
/*
非递归后序遍历
由于在访问当前树的根结点时,应先访问其左、右子树,
因而先将根结点入栈,接着将右子树也入栈,然后考虑左子树,重复这一过程直到某一左子树为空;
如果当前考虑的子树为空,若栈顶不为空,说明第二栈顶对应的树的右子树未处理,则弹出栈顶,
下次循环处理,并将一空指针入栈以表示其另一子树已做处理;
若栈顶也为空树,说明第二栈顶对应的树的左右子树或者为空,或者均已做处理,直接访问第二栈顶
的结点,访问完结点后,若栈仍为非空,说明整棵树尚未遍历完,则弹出栈顶,并入栈一空指针表示
第二栈顶的子树之一已被处理
*/
void PostOrderTraverse_1(BiTree T)
{
//定义栈
const int MAXSIZE = 100;
struct BiTNode *stack[MAXSIZE], *p;
int top= -1;//初始化栈
if (T == NULL)
return;
p = T;
while(1)
{
if(p)
{
stack[++top] = p;
stack[++top] = p->rchild;
p = p->lchild;
}
else if(!p)
{
p = stack[top--];
if(!p)
{
p = stack[top--];
printf("%c", p->data);
if(top == -1)
break;
p = stack[top--];
}
stack[++top] = NULL;
}
}
}
4、二叉树其他操作
(1)求二叉树结点个数
int BiTreeNodes(BiTree T)
{
if (T == NULL)
return 0;
else
return 1 + BiTreeNodes(T->lchild) + BiTreeNodes(T->rchild);
}
(2)求二叉树的高度
int BiTreeHeight(BiTree T)
{
if(T == NULL)
return 0;
else
{
int lh = BiTreeHeight(T->lchild);
int rh = BiTreeHeight(T->rchild);
return lh > rh ? lh + 1 : rh + 1;
}
}
(3)层次遍历二叉树
先将树的根节点入队,
如果队列不空,则进入循环
{
将队首元素出队,并输出它;
如果该队首元素有左孩子,则将其左孩子入队;
如果该队首元素有右孩子,则将其右孩子入队
}
如果队列不空,则进入循环
{
将队首元素出队,并输出它;
如果该队首元素有左孩子,则将其左孩子入队;
如果该队首元素有右孩子,则将其右孩子入队
}
void LevelOrderTraverse(BiTree T)
{
if (T == NULL)
return;
SqQueue<BiTNode*> q;
BiTNode*p;
q.EnQueue(T);
while (!q.QueueEmpty())
{
q.DeQueue(p);
visit(p);
if (p->lchild != NULL) q.EnQueue(p->lchild);
if (p->rchild != NULL) q.EnQueue(p->rchild);
}
}
层次遍历类似BFS。