二叉树的遍历——四种遍历方式:
一、先序遍历(PreOrderTraverse)
按照 根—左—右 的顺序进行的遍历
算法实现如下:
void Visit(BTNode * node){ // 访问某结点,这里仅输出结点中的字符
printf(“%c ”, node->data);
}
void PreOrderTraverse(BTNode * root) // 先序遍历
{
// 若root非空,则先访问root,再按照先序遍历方式访问其左子树
// 和右子树
if ( root->data ){ //判断非空
Visit(root) ; //访问该结点
PreOrderTraverse(root->lchild) ; //先序遍历的方式访问该结点的左子树
PreOrderTraverse(root->rchild) ; //先序遍历的方式访问该结点的右子树
}
}
二、中序遍历
按照 左—根—右 的顺序进行的遍历
void PostOrderTraverse(BTNode * root){
if(root){
if(root->data){
PostOrderTraverse(root->lchild);
Visit(root);
PostOrderTraverse(root->rchild);
}
}
}
三、后序遍历
按照 左—右—根 的顺序进行的遍历
void PostOrderTraverse(BTNode * root){
if(root){
if(root->data){
PostOrderTraverse(root->lchild);
PostOrderTraverse(root->rchild);
Visit(root);
}
}
}
四、层次遍历
借用队列的形式
思路:
1.根节点入队,然后出队,出队的时候传递值,执行访问,让它的子节点都入队;
2.根节点的子节点逐一出队,每出一个就传递值进行访问,并且引其子节点入队;
3.最后所有结点入队并出队,完成遍历。
算法实现如下:
#define MAX 100
typedef struct SqQueue{
BTNode QueueData[MAX];
int front, rear;
}SqQueue;
void InitQueue(SqQueue *q){
q->front = q->rear = 0;
}
int QueueFull(SqQueue *q){
if(q->rear == MAX -1)return 1;
else return 0;
}
void EinQueue(SqQueue *q, BTNode * Elmt){ //其中,q中记录了要进入的位置,elmt指向要加入队列的元素
int QueueFull(SqQueue *);
if(QueueFull(q)){printf("Team Full!"); exit(0);}
(q->QueueData[q->rear]) = *Elmt;
q->rear = (q->rear+1) % MAX;
}
int QueueEmpty(SqQueue *q){
return (q->rear - q->front <= 0);
}
BTNode* DeQueue(SqQueue *q){
void EinQueue(SqQueue*, BTNode *);
if(QueueEmpty(q)){printf("Team Empty!\n"); exit(0);}
BTNode *Elmt;
Elmt = &(q->QueueData[q->front]);
q->front = (q->front + 1) % MAX;
return Elmt;
}
void LevelTraverse(BTNode *root){
BTNode * pelmt; SqQueue queue, *qw; //总体思路:1.先让根节点1入队;(单独完成,在while循环之外)
qw = &queue; // 2.根节点1出队,同时其左右孩子2、3入队;
//先初始化队列 // 3.结点2出队,其左右孩子4、5入队
void InitQueue(SqQueue *); // 4.结点3出队,其左右孩子6、7入队;
InitQueue(qw); // 5.循环往复,直到队列为空;(while循环,条件是不为空)
//重复步骤2过程:
while(!QueueEmpty(qw)){ //队不为空,则循环
pelmt = DeQueue(qw); //pelmt中存放的是要加入队列的元素的地址
Visit(pelmt); //结点p出队,并对其进行访问
if(pelmt->lchild) EinQueue(qw, pelmt->lchild); //有左孩子时将其进队
if(pelmt->rchild) EinQueue(qw, pelmt->rchild); //有右孩子时将其进队
}
}
二叉树的重构:
在已知先序遍历和中序遍历的结果的情况下重构二叉树
对于二叉树的三种遍历,知二求一
(用ppos表示先序遍历的结果,ipos来表示中序遍历的结果)
BTNode* RestoreTree(char *ppos, char *ipos, int n){
BTNode *PTemp= (BTNode *)malloc(sizeof(BTNode));
char *findroot = ipos; //用来在中序遍历中寻找根节点,以用来区分左右子树
char *pMove = ipos; //用来移动
int lnum = 0, rnum = 0;
PTemp->data = *ppos; //ptemp是头节点,这是在给根节点赋值
PTemp->lchild = PTemp->rchild = NULL; //只是最底层的最左叶子节点,所以其r/lchild值为NULL;
//边界检测:
if(*ppos == NULL || *ipos == NULL) return PTemp;
//递归重构
while(PTemp->data != *pMove) //寻找根节点在中序遍历字符串的哪个具体位置
{
pMove++;
lnum++;
}
rnum = n - lnum-1; //右孩子总数 = 总数-左孩子-根节点
if(lnum > 0){
PTemp->lchild = RestoreTree(ppos+1, ipos, lnum);} //先不断递归这一条语句,直到ppos所指向的内容就是根节点中的内容
if(rnum > 0){
PTemp->rchild = RestoreTree(ppos+lnum+1, ipos+lnum+1, rnum);//这时所有的左子树已经出来了,剩下右边的,右边的同理
}
return PTemp;
}
其他与树相关的函数:
- 求结点总数:
int TotalNodes(BTNode *root){
if(!root) return 0;
else return 1 + TotalNodes(root->rchild) + TotalNodes(root->lchild);
}
- 求非叶子结点数的函数:
int TotalNoLeafNodes(BTNode * root){
if(!root) return 0; // 若root没有任何子树,则非叶子结点数也为0
else if(!(root->lchild || root->rchild)) return 0; //当root为空指针的时候,非叶子结点数为0
else//否则,root结点本身就是非叶子结点,还加上其子树的非叶子结点数
return 1 + TotalNoLeafNodes(root->rchild) + TotalNoLeafNodes(root->lchild);
}
- 求叶子结点数的函数:
int TotalLeafNodes(BTNode * root){
return TotalNodes(root) - TotalNoLeafNodes(root);
}
- 求树深的函数:
int TotalNoLeafNodes(BTNode * root){
if(!root) return 0; // 若root没有任何子树,则非叶子结点数也为0
else if(!(root->lchild || root->rchild)) return 0; //当root为空指针的时候,非叶子结点数为0
else//否则,root结点本身就是非叶子结点,还加上其子树的非叶子结点数
return 1 + TotalNoLeafNodes(root->rchild) + TotalNoLeafNodes(root->lchild);
}