目录
一、前、中,后序遍历
1.1基本思想
前序遍历:先访问根节点,再依次访问左子树和右子树
中序遍历:先访问左子树,再依次访问根节点和右子树
后序遍历:先访问右子树,再依次访问左子树和根节点
以前序遍历为例:
前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 2 5 6 4 1
1.2代码实现
前序遍历、中序遍历、后序遍历
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
1.3递归调用图
以前序遍历为例:
二、层序遍历
2.1基本思想
从上到下、从左到右逐层访问树的节点
二叉树的层序遍历采用队列的方式实现,根节点入队,出队时带入它的子节点入队(左孩子先入队,右孩子后入队),依次出队列。每次出队一个节点,就要带入它的子节点,直到所有的节点都出队完成,队列为空,循环结束。
2.2代码实现
void LevelOrder(BTNode* root)
{
Queue Q;
QInit(&Q);
//第一个结点入队
if (root)
{
QPush(&Q, root);
}
//遍历结点
while (!QEmpty(&Q))
{
//队首元素
BTNode* front = QFront(&Q);
//队首元素出队
QPop(&Q);
//根结点的值
printf("%d ", front->data);
//左右孩子入队
if (front->left)
QPush(&Q, front->left);
if (front->right)
QPush(&Q, front->right);
}
QDestroy(&Q);
}
三、判断一棵树是否为完全二叉树
3.1完全二叉树的概念
前K-1层是满二叉树,第K层从左到右没有空节点,满二叉树是完全二叉树的一种特殊情况
3.2基本思路
结合层序遍历的思想,如果遇到空节点后还存在非空节点,那么就不是一棵完全二叉树。
首先,根节点入队列,出队时带左右节点入队列,依次入队,直到遇到NULL跳出循环。接着判断NULL节点之后是否有非空节点,依次将队头元素出队,若遇到非空节点就返回false,若队列出空了说明NULL节点之后全是空节点,返回true。
3.3代码实现
int isFullBinaryTree(BTNode* root)
{
Queue Q;
QInit(&Q);
//第一个结点入队
if (root)
{
QPush(&Q, root);
}
else
{
return false;
}
//遍历结点
while (!QEmpty(&Q))
{
//队首元素
BTNode* front = QFront(&Q);
//队首元素出队
QPop(&Q);
if (front == NULL)
{
break;
}
//左右孩子入队
else
{
QPush(&Q, front->left);
QPush(&Q, front->right);
}
}
//判断
while (!QEmpty(&Q))
{
//队首元素
BTNode* front = QFront(&Q);
//队首元素出队
QPop(&Q);
//说明空节点是不连续的
if (front != NULL)
{
QDestroy(&Q);
return false;
}
}
return true;
QDestroy(&Q);
}
四、求节点个数
4.1基本思路
遍历节点,遇到一个节点就统计,再依次遍历它的左右节点。
4.2代码实现
值得注意的是,函数不带返回值的时候,由于每一次函数调用结束后栈帧会被销毁,如果size是定义在函数内部的话,它就不能把结果带回上一层函数调用了,所以要在函数外部创建一个变量size,通过指针对它进行改变。
void BinaryTressSize1(BTNode* root, int* psize)
{
if (root == NULL)
{
return;
}
(*psize)++;
BinaryTressSize1(root->left, psize);
BinaryTressSize1(root->right, psize);
}
不带返回值的函数用起来比较麻烦,我们可以改进用带返回值的函数来简化代码。
采用分治的思想,每一棵树的节点个数都等于左右子树相加再加1
int BinaryTressSize2(BTNode* root)
{
return root == NULL ? 0 : BinaryTressSize2(root->left) + BinaryTressSize2(root->right) + 1;
}
五、求树的高度
5.1基本思想
树的高度为左右子树最大的高度+根节点(1)
同样采用分治的思想,递归调用计算每一个节点左右子树的高度,再返回给上一层的函数调用
5.2代码实现
int BinaryTreeHeight(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftHeight = BinaryTreeHeight(root->left);
int rightHeight= BinaryTreeHeight(root->right);
if (leftHeight > rightHeight)
{
return leftHeight + 1;
}
else
{
return rightHeight + 1;
}
}
六、求第k层的节点个数
6.1基本思想
计算二叉树第k层的节点个数,由于我们无法直接找到第k层的位置,所以要采用相对位置的思想,即根节点所在的位置为第k层,递归访问左右子树,每访问一次k--,直到k=1,就找到了我们实际要的第k层,如果此时遇到的节点不为NULL就返回1,遇到NULL就返回0
6.2代码实现
int BinaryTreeKLevel(BTNode* root, int k)
{
if (root==NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
int leftk = BinaryTreeKLevel(root->left, k - 1);
int rightk = BinaryTreeKLevel(root->right, k - 1);
return leftk+rightk;
}
七、寻找值为x的节点
7.1基本思想
采用遍历的思想,遍历树的每一个节点,如果遍历过程中找到目标节点就返回节点指针,如果遍历结束后仍然没有找到目标节点就返回NULL。
假设树不存在目标节点,当且仅当左右子树都遍历结束了才能判断出该结果,也就是说NULL只能在左右子树都遍历完后才返回。
7.2代码实现
BTNode* BinaryTreeFind(BTNode* root, BTDatatype x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
BTNode* lret = BinaryTreeFind(root->left, x);
if (lret)
return lret;
BTNode* rret = BinaryTreeFind(root->right, x);
if (rret)
return rret;
return NULL;
}