炸裂!二叉树遍历的递归魔法与层序奥秘全解析,这波操作让你卷死算法岗面试官

💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 数据 结 构。
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:硬 核 数 据 结 构 与 算 法
✨代 码 趣 语:树 的 直 观 概 念 意 味 着 我 们 对 数 据 进 行 组 织。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在这里插入图片描述

         二 叉 树 是 计 算 机 科 学 中 重 要 的 数 据 结 构,在 搜 索、排 序 等 领 域 应 用 广 泛。遍 历 作 为 其 核 心 操 作,包 括 前 序、中 序、后 序 及 层 序 等 方 式,是 理 解 树 结 构 和 实 现 相 关 算 法 的 基 础。本 文 将 解 析 二 叉 树 遍 历 机 制 及 衍 生 应 用。


二 叉 树


         对 于 二 叉 树,普 通 无 序 二 叉 树 对 于 增 删 查 改 没 有 意 义, 原 因 是 结 构 复 杂 ,不 如 使 用 链 表。


定 义

         二 叉 树 遍 历 是 按 照 某 种 特 定 的 规 则,依 次 对 二 叉 树 中 的 结 点 进 行 相 应 的 操 作,并 且 每 个 结 点 只 操 作 一 次。一 棵 二 叉 树 分 为 根,左 子 树,右 子 树。


前 序 遍 历

定 义

在这里插入图片描述
         前 序 遍 历 又 被 称 为 前 根 遍 历,按 照 根 - - - 左子树 - - - 右 子 树 的 顺 序 依 次 访 问。

         如 图 所 示 的 二 叉 树 前 序 遍 历 的 访 问 顺 序 依 次 是 1 2 3 NULL(3的左子树) NULL(3的右子树) NULL(2的右子树) 4 5 NULL(5的左子树) NULL(5的右子树) 6 NULL(6的左子树) NULL(6的右子树)。
在这里插入图片描述

递 归 实 现

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
//     1
//  2     4 
//3     5   6
BTNode* CreatTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}
//前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}
int main
{
	BTNode* root = CreatTree();
	PreOrder(root);
	return 0;	
}

在这里插入图片描述

(1)根 节 点 不 为 NULL,访 问 并 输 出 根 节 点。
在这里插入图片描述
(2)1 的 左 子 树 不 为 NULL,遍 历 2。
在这里插入图片描述
(3)2 的 左 子 树 不 为 NULL,遍 历 3。
在这里插入图片描述
(4)3 的 左 子 树 为 NULL,不 遍 历 输 出 NULL,然 后 返 回 3。
在这里插入图片描述
(5)3 的 右 子 树 为 NULL,不 遍 历 输 出 NULL,然 后 返 回 3。
在这里插入图片描述
(6)返 回 3 之 后,回 退 到 2,2 的 右 子 树 为 空,不 遍 历 输 出 NULL,回 退 到 1。
在这里插入图片描述
(7)1 的 右 子 树 不 为 空,遍 历 1 的 右 子 树。
在这里插入图片描述
(8)4 的 左 子 树 不 为 空,遍 历 5。
在这里插入图片描述
(9)5 的 左 子 树 为 NULL,不 遍 历 输 出 NULL,然 后 返 回 5。
在这里插入图片描述
(10)5 的 右 子 树 为 NULL,不 遍 历 输 出 NULL,然 后 返 回 4。
在这里插入图片描述
(11)4 的 右 子 树 不 为 NULL,遍 历 输 出 6。
在这里插入图片描述
(12)6 的 左 子 树 为 NULL,输 出 NULL 后 返 回 6。
在这里插入图片描述
(13)6 的 右 子 树 为 NULL,输 出 NULL 后,返 回 根 节 点。前 序 遍 历 结 束。
在这里插入图片描述


中 序 遍 历

定 义

在这里插入图片描述
         中 序 遍 历 又 被 称 为 中 根 遍 历,按 照 左 子 树 - - - 根 - - - 右 子 树 的 顺 序 依 次 访 问。左 树 访 问 完 成 之 后 才 能 访 问 根。

         如 图 所 示 的 二 叉 树 中 序 遍 历 的 访 问 顺 序 依 次 是 NULL(3的左子树) 3 NULL(3的右子树) 2 NULL(2的右子树) 1 NULL(5的左子树) 5 NULL(5的右子树) 4 NULL(6的左子树) 6 NULL(6的右子树)

在这里插入图片描述

递 归 实 现

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
//     1
//  2     4 
//3     5   6
BTNode* CreatTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}
//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
int main
{
	BTNode* root = CreatTree();
	InOrder(root);
	return 0;	
}

在这里插入图片描述

(1)首 先 遍 历 3 的 左 子 树,为 空,输 出 NULL 后 返 回 3。
在这里插入图片描述
(2)遍 历 3 后,遍 历 3 的 右 子 树。
在这里插入图片描述
(3)3 的 右 子 树 为 空,不 遍 历,输 出 NULL 后,返 回 到 2。
在这里插入图片描述
(4)遍 历 2 后,遍 历 2 的 右 子 树。
在这里插入图片描述
(5)2 的 右 子 树 为 NULL,不 遍 历,输 出 NULL 后,返 回 根 节 点。
在这里插入图片描述
(6)根 节 点 不 为 空,遍 历 根 节 点 后 遍 历 5 的 左 子 树。
在这里插入图片描述
(7)5 的 左 子 树 为 空,不 遍 历,输 出 NULL 后 返 回 5。
在这里插入图片描述
(8)遍 历 5 后,遍 历 5 的 右 子 树。
在这里插入图片描述
(9)5 的 右 子 树 为 空,不 遍 历,输 出 NULL 后 返 回 4。
在这里插入图片描述
(10)遍 历 4 后,遍 历 6 的 左 子 树。
在这里插入图片描述
(11)6 的 左 子 树 为 空,不 遍 历,输 出 NULL 后 返 回 6。
在这里插入图片描述
(12)遍 历 6 后,遍 历 6 的 右 子 树。
在这里插入图片描述
(13)6 的 右 子 树 为 空,不 遍 历,输 出 NULL 后 返 回 根 节 点,中 序 遍 历 结 束。
在这里插入图片描述


后 序 遍 历

定 义

在这里插入图片描述

         后 序 遍 历 又 被 称 为 后 根 遍 历, 按 照 左 子 树 - - - 右 子 树 - - - 根 的 顺 序 依 次 访 问。

         如 图 所 示 的 二 叉 树 后 序 遍 历 的 访 问 顺 序 依 次 是 NULL(3的左子树) NULL(3的右子树) 3 NULL(2的右子树) 2 NULL(5的左子树) NULL(5的右子树) 5 NULL(6的左子树) NULL(6的右子树) 6 4 1

在这里插入图片描述


递 归 实 现

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
//     1
//  2     4 
//3     5   6
BTNode* CreatTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}
//后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}
int main
{
	BTNode* root = CreatTree();
	PostOrder(root);
	return 0;	
}

在这里插入图片描述

(1)首 先 遍 历 3 的 左 子 树,左 子 树 为 NULL,输 出 NULL。
在这里插入图片描述
(2)遍 历 3 的 右 子 树,为 空,不 遍 历,输 出 NULL 后 返 回 3。
在这里插入图片描述
(3)遍 历 3 后 返 回 2 的 右 子 树。
在这里插入图片描述
(4)2 的 右 子 树 为 空,输 出 NULL 后,返 回 2。
在这里插入图片描述
(5)遍 历 2 后 返 回 到 5 的 左 子 树。
在这里插入图片描述
(6)5 的 左 子 树 为 空,输 出 NULL 后,返 回 5 的 右 子 树。
在这里插入图片描述
(7)5 的 右 子 树 为 空,输 出 NULL 后,返 回 5。
在这里插入图片描述
(8)遍 历 5 后 返 回 到 6 的 左 子 树。
在这里插入图片描述
(9)6 的 左 子 树 为 空,输 出 NULL 后,返 回 6 的 右 子 树。
在这里插入图片描述
(10)6 的 右 子 树 为 空,输 出 NULL 后,返 回 6。
在这里插入图片描述
(11)遍 历 6 后 返 回 4。
在这里插入图片描述
(12)遍 历 4 后 返 回 根 节 点。
在这里插入图片描述
(13)遍 历 根 节 点。
在这里插入图片描述


层 序 遍 历

定 义

         从 根 节 点 出 发,按 “从 上 到 下、从 左 到 右” 的 层 次 顺 序,逐 层 访 问 树 的 节 点。比 如 二 叉 树 有 多 层 节 点,先 访 问 完 第 1 层(根 节 点),再 依 次 访 问 第 2 层、第 3 层 …… 同 层 节 点 按 从 左 到 右 顺 序 处 理。
在这里插入图片描述
         如 图 所 示 的 二 叉 树 层 序 遍 历 的 访 问 顺 序 依 次 是 1 2 3 4 5 6。


代 码 实 现

使 用 队 列 进 行 层 序 遍 历。
Queue.h

#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>

typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//销毁队列
void QueueDestory(Queue* pq);

//队尾入队列
void QueuePush(Queue* pq,QDataType x);

//队头出队列
void QueuePop(Queue* pq);

//获取队列中有效元素个数
int QueueSize(Queue* pq);

//检查队列是否为空
bool QueueEmpty(Queue* pq);

//取出队头的数据
QDataType QueueFront(Queue* pq);

//取出队尾的数据
QDataType QueueBack(Queue* pq);

//输出队列
void QueuePrint(Queue* pq);

void QueueMiddlePush(void(*pf)(Queue* pq, QDataType x), Queue* pq);

void QueueMiddle(Queue* pq, int num);

//保存队列到文件中
void QueueSave(Queue* pq);

//从文件中加载队列
void QueueLoad(Queue* pq);

//输出队列元素时删除队列元素
void QueuePrintDestory(Queue* pq);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1 

#include "Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
	pq->size = 0;
	QueueLoad(pq);
}

void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
	pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("newnode");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->head == NULL)
	{
		assert(pq->tail == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head != NULL);
	//方法1
	//QNode* next = pq->head->next;
	//free(pq->head);
	//pq->head = next;
	//if (pq->head == NULL)
	//{
	//	pq->tail = NULL;
	//}
	//方法2
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->tail = pq->head = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
	pq->size--;
}
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	//return pq->size == 0;
	return pq->head == NULL && pq->tail == NULL;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}



void QueuePrint(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	if (QueueEmpty(pq))
	{
		printf("队列为空\n");
		return;
	}
	else
	{
		while (cur)
		{
			printf("%d ", cur->data);
			cur = cur->next;
		}
		printf("\n");
	}
}
void QueueMiddlePush(void(*pf)(Queue* pq, QDataType x), Queue* pq)
{
	int x = 0;
	printf("请输入你想要插入的元素:>");
	scanf("%d", &x);
	pf(pq, x);
	QueuePrint(pq);
}

void QueueMiddle(Queue* pq, int num)
{
	if (num == 2)
	{
		if (QueueEmpty(pq))
		{
			printf("队列已空,无法出队\n");
		}
		else
		{
			printf("出队元素:%d\n", QueueFront(pq));
			QueuePop(pq);
		}
	}
	else if (num == 4)
	{
		if (QueueEmpty(pq))
		{
			printf("队列已空,无队头元素\n");
		}
		else
		{
			printf("队头元素:%d\n", QueueFront(pq));
		}
	}
	else if (num == 5)
	{
		if (QueueEmpty(pq))
		{
			printf("队列已空,无队尾元素\n");
		}
		else
		{
			printf("队尾元素:%d\n", QueueBack(pq));
		}
	}
	else
	{
		if (QueueEmpty(pq))
		{
			printf("队列已空\n");
		}
		else
		{
			printf("队列不为空\n");
		}
	}
}

void QueueSave(Queue* pq)
{
	assert(pq);
	FILE* pf = fopen("queue.txt", "w");
	if (pf == NULL)
	{
		perror("无法打开文件");
		return;
	}
	QNode* cur = pq->head;
	while (cur)
	{
		fprintf(pf, "%d ", cur->data);
		cur = cur->next;
	}
	fclose(pf);
	pf = NULL;
	printf("保存文件成功\n");
}

void QueueLoad(Queue* pq)
{
	assert(pq);
	FILE* pf = fopen("queue.txt", "r");
	if (pf == NULL)
	{
		//文件不存在或无法打开,可能是第一次运行
		return;
	}
	int data = 0;
	while (fscanf(pf, "%d", &data) != EOF)
	{
		QueuePush(pq, data);
	}
	fclose(pf);
	pf = NULL;
}

void QueuePrintDestory(Queue* pq)
{
	assert(pq);
	while (!QueueEmpty(pq))
	{
		printf("%d ", QueueFront(pq));
		QueuePop(pq);
	}
	printf("\n");
}

test.c

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "Queue.h"
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;
BTNode* BuyNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}
//     1
//  2     4 
//3     5   6
BTNode* CreatTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	BTNode* node7 = BuyNode(7);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}
void LevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);//Pop的是队列的节点,front保存的是树的节点
		printf("%d ", front->data);
		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
	}
	printf("\n");
	QueueDestory(&q);
}
int main()
{
	BTNode* root = CreatTree();
	LevelOrder(root);
	return 0;
}

在这里插入图片描述


代 码 执 行 流 程
(1)根 节 点 1 入 队。
(2)取 出 1,打 印 1,将 子 节 点 2 和 4 入 队(队 列:2 -> 4)。
(3)取 出 2,打 印 2,将 子 节 点 3 入 队(队 列:4 -> 3)。
(4)取 出 4,打 印 4,将 子 节 点 5 和 6 入 队(队 列:3 -> 5 -> 6)。
(5)取 出 3,打 印 3,无 子 节 点 入 队(队 列:5 -> 6)。
(6)取 出 5,打 印 5,无 子 节 点 入 队(队 列:6)。
(7)取 出 6,打 印 6,无 子 节 点 入 队(队 列 为 空)。
在这里插入图片描述


关 键 点
(1)队 列 确 保 了 节 点 按 层 次 顺 序 被 处 理。每 处 理 一 个 节 点 时,将 其 子 节 点 加 入 队 列 尾 部,保 证 下 一 层 的 节 点 在 当 前 层 的 所 有 节 点 处 理 完 后 才 被 处 理。
(2)front 存 储 的 是 当 前 从 队 列 中 取 出 的 树 节 点 的 指 针,通 过 该 指 针 可 以 访 问 树 节 点 的 数 据 和 子 节 点。


第 k 层 结 点 的 个 数

//第k层结点的个数
//根的第k层个数 = 左子树的k - 1层个数 + 右子树的k - 1层个数
int TreeLevel(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return TreeLevel(root->left, k - 1) +
		TreeLevel(root->right, k - 1);
}

关 键 点
(1)当 前 节 点 为 空 时,说 明 已 超 出 树 的 范 围,返 回 0。当 k 递 减 到 1 时,表 示 已 到 达 目 标 层,当 前 节 点 对 计 数 贡 献 为 1。
(2)递 归 计 算 左 子 树 的 第 k - 1 层 节 点 数 和 右 子 树 的 第k - 1 层 节 点 数。将 两 者 结 果 相 加,得 到 当 前 节 点 下 第 k 层 的 总 节 点 数。


复 杂 度
时 间 复 杂 度:O(n),需 要 遍 历 树 的 所 有 节 点。
空 间 复 杂 度:最 坏 情 况 下 为 O(h),其 中 h 是 树 的 高 度。


计 算 树 的 高 度

int TreeHigh(BTNode* root)
{
	//方法1
	//时间复杂度:O(N)
	//return root == NULL ? 0 : (TreeHigh(root->left) > TreeHigh(root->right) ?
	//	TreeHigh(root->left) + 1 : TreeHigh(root->right) + 1);
	//方法2
	//时间复杂度:O(N^2)
	//if (root == NULL)
	//	return 0;
	//return TreeHigh(root->left) > TreeHigh(root->right)
	//	? TreeHigh(root->left) + 1 : TreeHigh(root->right) + 1;
	//方法3
	if (root == NULL)
		return 0;
	int leftHeight = TreeHigh(root->left);
	int rightHeight = TreeHigh(root->right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

方 法 3 复 杂 度
时 间 复 杂 度:O(N)
空 间 复 杂 度:O(h),其 中 h 是 二 叉 树 的 高 度。


计 算 树 的 节 点 数 目

//方法1
//void Treesize(BTNode* root, int* size)
//{
//	if (root == NULL)
//	{
//		return;
//	}
//	Treesize(root->left, size);
//	Treesize(root->right, size);
//	(*size)++;
//}
//方法2
int Treesize(BTNode* root)
{
	return root == NULL ? 0 : Treesize(root->left) + Treesize(root->right) + 1;
}

原 理
         采 用 分 治 思 想,将 整 棵 树 的 节 点 数 分 解 为:
左 子 树 节 点 数 + 右 子 树 节 点 数 + 1(当 前 根 节 点)
递 归 终 止 条 件 :空 节 点 返 回 0。


复 杂 度
时 间 复 杂 度:O(N),每 个 节 点 恰 好 被 访 问 一 次。
空 间 复 杂 度:O(h),递 归 栈 的 最 大 深 度 为 树 的 高 度 h。

判 断 是 否 为 完 全 二 叉 树

bool IsCompleteTree(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		//有非空,说明后面节点不是完全连续
		if (front)
		{
			QueueDestory(&q);
			return false;
		}
	}
	QueueDestory(&q);
	return true;
}

         和 层 序 遍 历 类 似 这 段 代 码 需 要 使 用 队 列 来 判 断,这 里 省 略 了 Queue.h 和 Queue.c 这 两 个 文 件 的 代 码。


完 全 二 叉 树 的 特 性
非 空 节 点 必 须 连 续:在 层 序 遍 历 中,所 有 非 空 节 点 必 须 出 现 在 空 节 点 之 前。
遇 到 第 一 个 空 节 点 后,后 续 不 能 再 有 非 空 节 点:一 旦 在 层 序 遍 历 中 遇 到 一 个 空 节 点,那 么 队 列 中 剩 余 的 所 有 元 素 都 必 须 是 空 节 点。


         这 段 代 码 通 过 层 序 遍 历 将 所 有 节 点(包 括 空 节 点)加 入 队 列,当 第 一 次 遇 到 空 节 点 时 停 止 入 队,然 后 检 查 队 列 中 剩 余 元 素 是 否 全 为 空,以 此 判 断 是 否 为 完 全 二 叉 树。


销 毁 二 叉 树

void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

         这 里 推 荐 使 用 后 序 遍 历 如 果 使 用 前 序 遍 历 或 者 中 序 遍 历 无 法 找 到 当 前 的 左 右 子 树。
在这里插入图片描述


总 结

         通 过 解 析 二 叉 树 四 种 遍 历 方 式 及 节 点 计 数、高 度 计 算 等 操 作,揭 示 了 其 递 归 分 治 与 层 次 化 访 问 的 逻 辑。这 些 操 作 时 间 复 杂 度 与 节 点 数 相 关,空 间 复 杂 度 受 树 高 影 响。掌 握 基 础 操 作 后,可 进 一 步 学 习 平 衡 二 叉 树 等 变 种 结 构,提 升 对 树 结 构 的 理 解 与 应 用 能 力。

评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值