本篇针对链表类型的二叉树的遍历,数组类型的二叉树遍历就等于遍历数组
之前是以孩子兄弟的表示方法
这是是左右孩子的表示方法
typedef char BTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType data;
}BTNode;
遍历顺序:根节点-左子节点-右子节点
// 二叉树前序遍历
void PreOrder(BTNode* root)
{
if (root == NULL) {
printf("NULL");
return;
}
printf("%c ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
遍历顺序:左子节点-根节点-右子节点
// 二叉树中序遍历
void InOrder(BTNode* root)
{
if (root == NULL) {
printf("NULL");
return;
}
InOrder(root->left);
printf("%c ", root->data);
InOrder(root->right);
}
遍历顺序:左子节点-右子节点-根节点
// 二叉树后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL) {
printf("NULL");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%c ", root->data);
}
链式结构且左孩子右孩子
typedef char BTDataType;
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
BTDataType data;
}BTNode;
弄一个简单的
BTNode* BuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
printf("malloc fail\n");
exit(-1);
}
node->data = x;
node->left = node->right = NULL;
return node;
}
BTNode* CreatBinaryTree()
{
BTNode* nodeA = BuyNode('A');
BTNode* nodeB = BuyNode('B');
BTNode* nodeC = BuyNode('C');
BTNode* nodeD = BuyNode('D');
BTNode* nodeE = BuyNode('E');
BTNode* nodeF = BuyNode('F');
BTNode* nodeG = BuyNode('G');
nodeA->left = nodeB;
nodeA->right = nodeC;
nodeB->left = nodeD;
nodeB->right = nodeE;
nodeC->left = nodeF;
nodeC->right = nodeG;
return nodeA;
}
int main()
{
BTNode* tree = CreatBinaryTree();
//前序
PreOrder(tree);
printf("\n");
//中序
InOrder(tree);
printf("\n");
//后序
PostOrder(tree);
printf("\n");
return 0;
}
测试结果
递归实现,遍历每一个节点,不是空节点就+1
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
/*if (root == NULL)
{
return 0;
}
return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;*/
return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
也是递归,但注意条件
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
//判断是否是叶子节点
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
//k = 0;
assert(k >= 1);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
从下面的NULL
开始往上递归,到节点的倒数第一层+1
// 二叉树深度/高度
int BinaryTreeDepth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftdepth = BinaryTreeDepth(root->left);
int rightdepth = BinaryTreeDepth(root->right);
return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
}
这里的逻辑我觉得很好理解,但写出来,写清楚不太好想
首先一遇到NULL,return NULL;
如果是root == x,那么return root;
重点是怎么递归下去
像这种不是空函数,有返回值的递归的话,每次需要保存之前的值
如果没找到,return NULL;
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
//左孩子
BTNode* leftRet = BinaryTreeFind(root->left, x);
if (leftRet)//如果是NULL的话就进不去
{
return leftRet;
}
//z
BTNode* rightRet = BinaryTreeFind(root->right, x);
if (rightRet)//如果是NULL的话就进不去
{
return rightRet;
}
return NULL;
}
首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历
因为用队列实现
所以我们需要队列的一些接口
如果遇到无法打开源文件这种情况
右击你的项目找到 属性
接着按照 配置属性 - C/C++ - 附加包含目录
加添文件所在目录
//层序遍历
void BinaryTreeLevelOder(BTNode* root)
{
if (root == NULL)
{
return;
}
//创建队列
Queue q;
QueueInit(&q);
//先放入根
QueuePush(&q, root);
QueueDestroy(&q);
}
这里存在一个问题原来的QueuePush
//进队
void QueuePush(Queue* pq, QDataType x);
这里的QDataType
的类型,在队列里面是int
typedef int HPDataType;
现在我们要改成BTNode*
但是如果直接改会出现很多错误
这些错误都是因为导致的
typedef BTNode* HPDataType;
因为文件前面没有声明BTNode*
这个类型
所以需要先声明
可以右击转到定义
快速找到原来的定义的地方,然后复制去声明
//层序遍历
void BinaryTreeLevelOder(BTNode* root)
{
if (root == NULL)
{
return;
}
//创建队列
Queue q;
QueueInit(&q);
//先放入根
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
//保存后再pop用来打印
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%c", front->data);
//根出去,再入左右孩子
if(front->left)//非空再入队列
QueuePush(&q, front->left);
if(front->right)
QueuePush(&q, front->right);
}
QueueDestroy(&q);
}
测试结果:
在遇到NULL时候需要注意
这里NULL
没有进去,如果在push
的前面没有进行什么操作的话,判断不出来是不是完全二叉树
我们可以发现
如果是完全二叉树,NULL之后不会有数据
如果是不完全二叉树,NULL之后还会有数据
所以我们在第一个NULL
停止层序遍历,再继续进行第二个层序遍历,如果后面都是NULL
,就是完全二叉树
这里我们可以把 NULL当作一个队列中元素,进行插入
因为队列节点结构体是,思路要清晰不能因为root = NULL
而去想,如果root->left == NULL
,会不会报错
typedef BTNode* QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;//如果这里为NULL,那也没有关系,我们只需要它来作为一个判断
}QueueNode;
完全二叉树和不完全二叉树第一次遇到NULL时候队列情况比较
//判断是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
//创建队列
Queue q;
QueueInit(&q);
//先放入根
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
//保存后再pop
BTNode* front = QueueFront(&q);
QueuePop(&q);
//根出去,再入左右孩子
if (front == NULL)//非空再入队列
{
break;
}
else
{
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
}
while (!QueueEmpty(&q))
{
//保存后再pop
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front != NULL)
{
return false;
}
}
QueueDestroy(&q);
return true;
}
测试: