前言
前面我们学习的几种遍历方式主要有:前序遍历,中序遍历,层序遍历。这几种遍历方式都是采用递归进行实现的,而今天学习的层序遍历采取的是非递归的方式进行实现,主要是迭代的方式进行实现,同时需要使用队列结构进行辅助实现。
层序遍历
层序遍历其实也可以称为广度优先遍历,就是只按照顺序依次往下进行遍历的过程。
层序遍历的思想:利用一个队列结构进行辅助实现,首先判断对应的树是否为空树,如果为空树,则直接返回,如果不为空树,则先将根节点的值入队列,进行循环遍历,当队列不为空时,先取出队头元素,再将队头元素出队列,此时可以先访问取出的队头元素中存储的值,再判断队头元素是否存在左右子树,若存在左子树,则将左子树入队列,若存在右子树,则将右子树入队列。再进行下一轮循环
- 代码实现
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 树的结构
typedef int TreeDataType;
// 树的结点
typedef struct TreeNode
{
TreeDataType val;
struct TreeNode* left;
struct TreeNode* right;
};
typedef TreeNode* QDataType;
// 定义队列中结点的结构
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* front;
QueueNode* tail;
}Queue;
void QueueInit(Queue* q);
void QueuePush(Queue* q, QDataType val);
QDataType QueueFront(Queue* q);
bool QueueEmpty(Queue* q);
void QueueDestroy(Queue* q);
void QueuePop(Queue* q);
// 层序遍历
void LevelOrder(TreeNode* root)
{
if (root == NULL)
{
// 空树
return;
}
// 非空树
// 将根节点入队列
// 创建一个队列
Queue q;// 创建队列
QueueInit(&q);// 对队列进行初始化
QueuePush(&q, root);// 将根节点的值先插入队列
while (!QueueEmpty(&q))
{
// 当队列不为空的时候需要进行遍历处理‘
TreeNode* front = QueueFront(&q);
QueuePop(&q);
// 判断取到的结点是否存在左右子树
if (front->left)
{
QueuePush(&q, front->left);
}
if (front->right)
{
QueuePush(&q, front->right);
}
// 从队列中取到的值一定是不为空的
// 访问取到的数据
printf("%d ", front->val);
}
printf("\n");
QueueDestroy(&q);
}
层序遍历的应用:判断一棵树是否为完全二叉树
bool TreeComplete(TreeNode* root)
{
if (root == NULL)
{
return true;
}
// 非空树
// 创建队列
Queue q;
QueueInit(&q);
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
// 取队头元素
TreeNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
break;
}
QueuePush(&q,front->left);
QueuePush(&q,front->right);
}
// 当程序走到这里,说明遇到了NULL
while (!QueueEmpty(&q))
{
TreeNode* front = QueueFront(&q);
QueuePop(&q);
// 遇到非空的结点,说明在前面遇到的空结点是在非空结点前面出现,则说明该树不是完全二叉树
if (front)
{
QueueDestroy(&q);
return false;
}
}
// 如果上述中没有访问非空结点,则说明第一次遇到的空结点就已经访问完所有的非空结点
QueueDestroy(&q);
return true;
}
思路分析:
使用一个队列结构进行辅助实现,思想与层序遍历差不多,只不过这道题中,空树仍然需要进队列,当第一次遍历到空结点的时候,结束第一个循环。接着遍历队列中的结点,如果遍历到非空结点,则说明上面的空结点存在于有效结点之间,则该树不是完全二叉树。如果没有遍历到非空,则说明第一次遍历到的空结点是在有效结点之后,说明该树是完全二叉树。