一、二叉树概念
一颗二叉树是结点的一个有限集合,该集合:
(1)为空
(2)由一个根节点加上两棵称为左子树和右子树的二叉树组成
从上图可以看出:
(1)二叉树不存在度大于2的结点
(2)二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
二、特殊二叉树
1、满二叉树:一个二叉树,如果每一层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树层数为k,且总结点数是2^k-1,则它就是满二叉树。
2、完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一 一对应时称为完全二叉树。要注意满二叉树是一种特殊的完全二叉树。
举例:
三、二叉树的性质
1、若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点。
2、若规定根节点的层数为1,则深度为h的二叉树的最大结点数为2^h-1。
3、对任何一颗二叉树,如果度为0的叶子结点个数为n0,度为2的分支结点个数为n2,则有n0=n2+1。
4、若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=log2(n+1)(ps:2为底,n+1为对数)。
5、对于具有n个结点的完全二叉树,如果按照从上至下,从左至右的顺序对所有结点从0开始编号,则对于序号为i的结点有:
(1)若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
(2)若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
(3)若2i+1<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
四、二叉树的基本操作
1、定义数据结构
typedef char DataType;
typedef struct BinaryTreeNode
{
DataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
2、二叉树的遍历
(1)前序遍历----先访问根节点,再访问左子树,最后访问右子树
对上述二叉树进行前序遍历得到的序列为:123456
//前序遍历递归方法
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
return;
printf("%c ", root->data);
BinaryTreePrevOrder(root->left);
BinaryTreePrevOrder(root->right);
}
中序遍历和后序遍历与前序遍历大同小异,只需注意访问顺序即可
(2)中序遍历----先访问左子树,再访问根节点,最后访问右子树
对上述二叉树进行中序遍历得到的序列为:321546
// 中序遍历递归方法
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
return;
BinaryTreeInOrder(root->left);
printf("%c ", root->data);
BinaryTreeInOrder(root->right);
}
(3)后序遍历----先访问左子树,再访问右子树,最后访问根节点
对上述二叉树进行后序遍历得到的序列为:325641
// 后序遍历递归方法
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
return;
BinaryTreePostOrder(root->left);
BinaryTreePostOrder(root->right);
printf("%c ", root->data);
}
3、通过前序遍历构建二叉树
给出二叉树前序遍历序列(NULL用空格或者其他字符代替)
//创建新节点
BTNode* _BuyBTNode(DataType data)
{
BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
if (newNode == NULL)
{
printf("_BuyBTNode:malloc失败\n");
exit(0);
}
newNode->data = data;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
//利用前序遍历构建二叉树
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* _BinaryTreeCreate(DataType* a, int n, int* pi)
{
if (*pi < n && a[*pi] != '#')
{
BTNode* newNode = _BuyBTNode(a[*pi]);
(*pi)++;
newNode->left = _BinaryTreeCreate(a, n, pi);
(*pi)++;
newNode->right = _BinaryTreeCreate(a, n, pi);
return newNode;
}
return NULL;
}
BTNode* BinaryTreeCreate(DataType* a, int n)
{
int index = 0;
return _BinaryTreeCreate(a, n, &index);
}
4、层序遍历
层序遍历就是对二叉树逐层访问。
对上述二叉树进行层序遍历得到的序列为:124356
实现思想:通过利用队列先进先出的性质,先将根节点入队,当队列不为空时,弹出队首元素,将元素的左右孩子入队(左右孩子不为空时),直到队列为空,即遍历结束。
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
if (root == NULL)
return;
Queue q;
//创建队列
QueueCreate(&q);
//将根节点入队
QueuePush(&q, root);
//循环,弹出队首元素,并将队首元素的左孩子与右孩子入队
while (!QueueEmpty(&q))
{
BTNode* pcur = QueueGetFront(&q)->data;
printf("%c ", pcur->data);
if (pcur->left != NULL)
QueuePush(&q, pcur->left);
if (pcur->right != NULL)
QueuePush(&q, pcur->right);
QueuePop(&q);
}
QueueDestroy(&q);
}
5、其他操作
结合二叉树的性质实现
(1)二叉树节点个数
根节点(1个)+左子树节点个数+右子树节点个数。遇到NULL时返回0。
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
return 0;
return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
(2)二叉树高度
根节点高度(1)+Max(左子树高度,右子树高度)。遇到NULL时返回0。
//二叉树高度
int BinaryTreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int LeftHeight = BinaryTreeHeight(root->left);
int RightHeight = BinaryTreeHeight(root->right);
return LeftHeight > RightHeight ? LeftHeight + 1 : RightHeight + 1;
}
(3)二叉树叶子节点个数
叶子节点性质:左右子树都为NULL。
// 二叉树叶子节点个数
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);
}
(4)二叉树第k层节点个数
给出层数k,若k>0则向下遍历,直至k=1(到达目标层),若该节点为NULL,返回0,否则返回1。
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if ((k < 1) || (root == NULL))
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
(5)二叉树查找节点值为x的节点
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, DataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* res;
(res = BinaryTreeFind(root->left, x)) || (res = BinaryTreeFind(root->right, x));
return res;
}
(6)判断二叉树是否为完全二叉树
实现思想:找出第一个不满足满二叉树性质的节点,若该节点只有右子树没有左子树,返回0。
否则,继续向下遍历,检查后续节点是否有左右子树,若有返回0,无返回1。
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
if (root == NULL)
return 1;
Queue q;
QueueCreate(&q);
QueuePush(&q, root);
//标志是否找到第一个非满二叉树的节点
int flag = 0;
//1代表是完全二叉树,0代表不是
int res = 1;
while (!QueueEmpty(&q))
{
BTNode* pcur = QueueGetFront(&q)->data;
QueuePop(&q);
if (flag == 0)
{
if (pcur->left == NULL || pcur->right == NULL)
{
//当至少有一个孩子为空时,改节点为第一个非满二叉树节点
flag = 1;
if (pcur->left == NULL && pcur->right != NULL)
{
//当有右孩子无左孩子,res置0,退出循环
res = 0;
break;
}
}
if (pcur->left != NULL)
QueuePush(&q, pcur->left);
if (pcur->right != NULL)
QueuePush(&q, pcur->right);
}
else
{
if (pcur->left != NULL || pcur->right != NULL)
{
//后续节点如果有孩子,比不是完全二叉树
res = 0;
break;
}
}
}
QueueDestroy(&q);
return res;
}
6、二叉树的销毁
形参为二级指针,若为一级指针,根节点销毁不了。
销毁时先销毁左右子树,若根节点先被销毁,左右子树无法被访问,出现错误。
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
assert(root != NULL);
if (*root == NULL)
return;
BinaryTreeDestory(&(*root)->left);
BinaryTreeDestory(&(*root)->right);
free(*root);
*root = NULL;
}