前言
在二叉树的四种遍历中,唯独层序遍历是最特殊的, 他用的不是递归的思路,而是队列,在部分面试题里也出现不少
一、层序遍历是什么?
层序遍历就是按层从上到下,每层按一定顺序对树的节点进行遍历
如图所示:
他通过队列的形式,输入第一个节点到队头后,随着他的pop,他会将他的左右孩子push进入队列
每当一个节点被pop,他的左右孩子就会被push进队列中
便达到了按每层的顺序对树的节点进行遍历
二、层序遍历的构建
- 得先有个队列
- 将树的根节点push到队头
- ↓↓↓
- 记录下对头的那个节点front
- pop队头成员
- 利用front将该结点的左右孩子一起存到队列中
- ↑↑↑
- 箭头包围的位置进行循环,直到树被全部pop掉了
三、样例代码
//层序遍历
void LevelOrder(BTNode* root)
{
//创建队列
Queue q;
//初始化队列
QueueInit(&q);
if (root != NULL)
{
//将第一个根节点放到堆头
QueuePush(&q, root);
}
//只要树还有节点,那就一直循环
while (!QueueEmpty(&q))
{
//记录对头的节点的指针
BTNode* front = QueueFront(&q);
printf("%d ", front->val);
//删除对头的指针
QueuePop(&q);
//左右孩子放到队列中
if (front->left != NULL)
{
QueuePush(&q, root->left);
}
if (front->right != NULL)
{
QueuePush(&q, root->right);
}
}
printf("\n");
QueueDestroy(&q);
}
以上自己创造的函数和结构体都在这里
可通过蓝色高亮来查看
队列
typedef BTNode* QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail\n");
return;
}
newnode->data = x;
newnode->next = NULL;
if (pq->ptail == NULL)
{
assert(pq->phead == NULL);
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
// 1、一个节点
// 2、多个节点
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
// 头删
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->phead->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
/*return pq->phead == NULL
&& pq->ptail == NULL;*/
return pq->size == 0;
}
树节点的结构体
typedef struct BinaryTreeNode
{
BTDataType val;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
疑问补充:
为什么指针front已经被QueuePop了,还能通过指针访问左右孩子?
答:在队列中,每个成员都由两个元素构成
一个是树的节点指针,
另一个是下一个队列中下一个成员的指针
一开始我们已经将对头成员的树节点指针用记录front下来了
QueuePop之后,队列的头节点就会被pop然后释放
但是被释放的只是那个队列的指针(QNode *),而不是树节点的指针(BTNode * );
所以仍然可以利用front找到他的左右孩子
总结
以上就是今天要讲的内容,本篇文章着重讲解了二叉树之层序遍历的原理和思路,在力扣以及牛客网中还会有很多类似的题目,今后将会持续更新,敬请关注!