本篇博客梳理二叉树链式结构
明确:二叉树是递归定义的
递归的本质:当前问题+子问题,返回条件是最小规模的子问题
一、二叉树的遍历
1.前序、中序与后序遍历
(1)前序:根->左子树->右子树(每个子树也满足这个遍历顺序,下同)
(2)中序:左子树->根->右子树
(3)后序:左子树->右子树->根
分析前序遍历
递归展开图如下,红色箭头表示递推,绿色箭头表示回归
// 二叉树前序遍历:根-左子树-右子树
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
BinaryTreePrevOrder(root->leftChild);
BinaryTreePrevOrder(root->rightChild);
}
前序遍历结果:
1的左右子树是两个红框,红框内的树仍旧满足前序遍历
// 二叉树中序遍历:左子树-根-右子树
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
BinaryTreeInOrder(root->leftChild);
printf("%d ", root->data);
BinaryTreeInOrder(root->rightChild);
}
中序遍历结果:
// 二叉树后序遍历:左子树-右子树-根
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
BinaryTreePostOrder(root->leftChild);
BinaryTreePostOrder(root->rightChild);
printf("%d ", root->data);
}
后序遍历结果:
2.层序遍历(广度优先遍历【BFS】)
逐层访问二叉树的结点
① 算法思想:用队列辅助,上一层带下一层
② 具体操作:队列结点的data存指向二叉树结点的指针,一个结点出列,该节点孩子马上入列(空结点不入列)
③ 画图分析:
代码实现如下,队列相关的函数可在4.1中找到
// 层序遍历
//用队列辅助,每个节点当中存指向二叉树对应结点的指针
void BinaryTreeLevelOrder(BTNode* root)
{
Queue queue;
QueueInit(&queue);
//注意:队列中链表节点的data要改成BTNode*的指针
//根节点先入列
QueuePush(&queue, root);
while (!QueueEmpty(&queue))
{
//一个结点出列,带着其孩子入列,空结点不入列
BTNode* front = QueueFront(&queue);
if (front->leftChild != NULL)//左孩子不为空则入列
{
QueuePush(&queue, front->leftChild);
}
if (front->rightChild != NULL)//右孩子不为空则入列
{
QueuePush(&queue, front->rightChild);
}
printf("%d ", QueueFront(&queue)->data);
QueuePop(&queue);//结点出列
}
QueueDestroy(&queue);
}
二、结点个数与高度等
二叉树链式结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* leftChild;
struct BinaryTreeNode* rightChild;
}BTNode;
1.二叉树结点个数:int BinaryTreeSize(BTNode* root);
节点个数返回值如下:
- 空:return 0
- 不为空:return 左子树结点数+右子树结点数+1
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->leftChild == NULL && root->rightChild == NULL)
return 1;
else
return BinaryTreeSize(root->leftChild) +
BinaryTreeSize(root->rightChild) + 1;
}
2.二叉树叶子结点个数:int BinaryTreeLeafSize(BTNode* root);
叶子结点个数返回值如下
- 空:return 0
- 叶子:return 1
- 非叶子:return 左子树叶子数+右子树叶子数
//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->leftChild == NULL && root->rightChild == NULL)
return 1;
return BinaryTreeLeafSize(root->leftChild)
+ BinaryTreeLeafSize(root->rightChild);
}
3.二叉树第k层结点个数int BinaryTreeLevelKSize(BTNode* root, int k);
- 空:return 0
- 非空且k==1:return 1
- 非空且k>1:研究左子树第k-1层+右子树第k-1层
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (root && k == 1)
return 1;
return BinaryTreeLevelKSize(root->leftChild, k - 1) +
BinaryTreeLevelKSize(root->rightChild, k - 1);
}
4.判断二叉树是否为完全二叉树:int BinaryTreeComplete(BTNode* root);
① 算法思想:层序遍历
② 具体操作:层序遍历,把空也带入队列,第一个空出列之后就开始检查,如果队列中还有非空元素,就不是完全二叉树
//判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
if (root == NULL)
return 0;
//用层序遍历,把所有数据(包括NULL)也带入队列
//当第一个空出列之后,开始判断,如果队列中还有非空就不是完全二叉树
Queue queue;
QueueInit(&queue);
QueuePush(&queue, root);//根先入队列
while (!QueueEmpty(&queue))
{
BTNode* front = QueueFront(&queue);
if (front != NULL)
{
QueuePop(&queue);
QueuePush(&queue, front->leftChild);
QueuePush(&queue, front->rightChild);
}
//遇到第一个NULL在队头就开始检查
if (front == NULL)
{
break;
}
}
//注意:NULL指针在队列当中,队列不是空
while (!QueueEmpty(&queue))
{
BTNode* front = QueueFront(&queue);
if (front == NULL)
{
QueuePop(&queue);
}
if (front != NULL)//发现队列当中还有不为NULL的元素,就不是完全二叉树
return 0;
}
return 1;
}