层次遍历二叉树
文章目录
- 层次遍历二叉树
- ♥ 做法
- ♥算法构思
- ♥ 数据结构设计
- ♥ 层次遍历过程
- ♥ 算法实现
- 应用:用层次遍历求路径之逆
- ♥ 问题
- ♥ 解题思路:
- ♥ 算法框架:
- ♥ 算法实现
♥ 做法
▪ 逐层进行访问
▪ 对某一层的节点访问完后,再按照其访问次序对各个节点的左、右孩子顺序访问。
♥算法构思
既然我们了解了层次遍历的次序, 那接下来就是层次遍历的设计,我们是逐层遍历,在同一层次上面的节点,先遍历的节点,其孩子在下一层也有优先权,所以我们在遍历到一个节点时,其孩子也要进入排队的序列。我们根据优先权,先进来要求先出,所以我们可以采用队列的形式,逐层的去存储要出队遍历的节点。
唯一需要注意的就是,当遍历一个节点的时候,我们要把其孩子入队,以方便下一层节点的有序遍历。
当然,由于节点的个数不确定,我们可以采用环形队列的方式进行数据结构存储。
♥ 数据结构设计
▪ 先访问的节点,其左、右孩子也要先访问
▪ 先进先出
▪ 用队列实现
▪ 用环形队列实现
♥ 层次遍历过程
▪ 先将根节点进队
▪ 节点出队并访问,再将左右孩子入队
▪ 循环,直到队空
♥ 算法实现
//传入要层次遍历的树
void LevelOrder(BTNode *b)
{
//定义遍历节点的指针
BTNode *p;
//定义存储遍历节点次序的数组队
BTNode *qu[MaxSize];
//定义队的队头和队尾
int front,rear;
//初始化队列
front = rear = -1;
//我们先将根节点入队
rear++;
qu[rear]=b;
//接下来就是遍历节点,然后将其孩子加入队列,按层次遍历的过程了
while(front!=rear)
{
//从队列中出列一个节点*p,访问它;
front = (front+1)%MaxSize;
p = qu[front];
printf("%c",p->data);
//若它有左孩子节点,将其左孩子节点进队
if(p->lchild!=NULL)
{
rear = (rear+1)%MaxSize;
qu[rear] = p->lchild;
}
//若它有右孩子节点,将右孩子节点进队
if(p->rchild!=NULL)
{
rear = (rear+1)%MaxSize;
qu[rear] = p->rchild;
}
}
}
应用:用层次遍历求路径之逆
♥ 问题
二叉树采用二叉链存储结构,设计算法输出从根节点到每个叶子节点的路径之逆
♥ 解题思路:
既然要求路径之逆,那么就需要让节点知道其双亲是谁, 我们可以通过遍历给每个节点编号,定义每个节点的双亲信息。既然要定义存储其双亲信息,那我们我们就需要遍历二叉树,那就非层次遍历莫属了,因为层次遍历没有递归遍历的冗余信息,没有先序遍历、中序遍历、后续遍历来回操作的烦恼,只需要按部就班的进行进队和出队节点即可,方便我们进行遍历每个节点。并且我们在处理一个节点的时候,会把其孩子入队,此时我们就可以把节点的信息,告诉其孩子,并存储到数组里面,方便后续我们进行求路径之逆。
♥ 算法框架:
结合层次遍历的特点,给出解题框架
▪ 采用非环形顺序队列qu
▪ 层次遍历二叉树
▪ 将所有已访问过的节点指针进队,并在队列中保存双亲节点的位置
▪ 当找到一个叶子结点时,在队列中通过双亲节点的位置输出根节点到该叶子节点的路径之逆。
void AllPath2(BTNode*b)
{
定义队列;
根节点入队;
while(队列不空)
{
if(检查是否是叶子节点)
{
如果是,就根据标记输出叶子节点到跟节点的路径
}
}
//继续遍历二叉树,把每一个节点赋值到数组里面,并将双亲信息给孩子
左孩子先入队;
右孩子入队;
}
__________________________________________________________________________________
我们不用担心,已经填入的数组的节点不够用,不足以找到其双亲,因为我们是按照层次遍历的二叉树,
如果遍历到一个节点是叶子节点,那其双亲一定在数组里面,我们也一定可以找到,因为我们就是根据其
双亲才将节点入队访问的,都是按照层次进行访问的,其上层一定在数组里面,边访问,边输出即可。
♥ 算法实现
//传入二叉树
void AllPath2(BTNode *b)
{
//定义访问的节点的指针
BTNode *q;
//定义存储双亲信息的数组节点
struct snode
{
//存储节点的值
BTNode *node;
//存储节点双亲在数组里的位置
int parent;
}qu[MaxSize];
//因为我们是按照层次进行遍历的,所有节点都是按顺序填入数组的,我们可以将此存储双亲信息的数组当成队列去使用
//我们此次没有出队节点的需要,所以不会破坏队列的数据
//初始化队列
int front,rear,p;
front = rear = -1;
//入队根节点
rear++;
qu[rear].node = b;
qu[rear].parent = -1; //根节点无双亲,所以记作-1
//下面开始遍历访问节点,并填入数组,如果遇到叶子节点,根据双亲信息输出即可
//队列不空
while(front!=rear)
{
//访问队头节点
front++;
q = qu[front].node;
//判断叶子节点,输出路径之逆
if(q->lchild==NULL && q->rchild==NULL)
{
//通过上面的验证,说明队首节点就是叶子节点
p = front;
while(qu[p].parent!= -1)
{
printf("%c->",qu[p].node->data);
//遍历赋值叶子节点路径上的双亲数组序号
p = qu[p].parent;
}
printf("%c\n",qu[p].node->data);
}
//左孩子入队
if(q->lchild!=NULL)
{
rear++;
//入队的节点
qu[rear].node = q->lchild;
//入队节点的双亲是队首节点
qu[rear].parent = front;
}
//右孩子入队
if(q->rchild!=NULL)
{
rear++;
//入队的节点
qu[rear].node = q->rchild;
//入队节点的双亲是队首节点
qu[rear].parent = front;
}
}
}