前面我们对二叉树的顺序结构(堆:一种二叉树)进行了一个实现,这里,我们将对二叉树的链式结构进行一个实现。
1. 二叉树的基本功能
实现一颗二叉树,我们首先要知道二叉树的基本功能是什么,这里我列举了一些简单的功能:
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树 cv
BTNode* BinaryTreeCreate(BTDataType* a, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);
2. 二叉树的基本实现
2.1. 二叉树的前序遍历
前序遍历也叫 dfs (深度优先遍历)这里我们先通过图示了解一下前序遍历:
这个遍历得到的结果就是 1->2->3->4->5->6 ,他先从 1 开始然后递归左子树到 2 然后递归左子树到 3 然后递归左子树到 NULL,由于左子树为NULL,所以左子树遍历完毕,然后以 3 为根节点遍历右子树,右子树也为空随即返回。这样以3为根节点的二叉树就遍历完毕。然后回到以 2 为根节点的树进行遍历,2 树的右子树也为NULL,随即返回。这样,以 1 为根节点的二叉树的左子树就遍历完毕。然后就开始遍历 1 树的右子树4 ,同样的先遍历 4 树的左子树 5 然后遍历4树的右子树 6 .这样一颗二叉树的前序遍历就完成了。二叉树的前序遍历总结几个字就是: 根->左->右
这里附上参考代码,这里为了表示遍历到了NULL的节点,我将空节点打印为了“N” :
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
else
{
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
}
2.2. 二叉树的中序遍历
知道了二叉树的前序遍历,二叉树的中序遍历也是比较简单的,这里附上图示:
中序遍历总结几个字就是: 左->根->右
这里附上参考代码:
void BinaryTreeInOrder(BTNode* root)
{
if (root)
{
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
else
printf("N ");
}
2.3 二叉树的后序遍历
后序遍历总结几个字就是: 左->右->根 。 通过前面两个遍历,这里看图应该能理解:
参考代码如下:
void BinaryTreePostOrder(BTNode* root)
{
if (root)
{
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ",root->_data);
}
else
printf("N ");
}
2.4 二叉树的层序遍历
前面我们了解到的二叉树都是递归式的遍历,这里我们将要了解二叉树的一种非递归遍历---层序遍历,层序遍历也叫 bfs (广度优先遍历)。广度优先遍历很简单理解,就是一层一层遍历,大概的遍历过程如下:
要实现层序遍历有多种方法,我们这里提供一种相对简单的方法。首先我们需要借助队列进行遍历,这里我们附上参考代码进行说明:
void BinaryTreeLevelOrder(BTNode* root)
{
Queue Q; //构建一个队列
QueueInit(&Q);//初始化队列
QueuePush(&Q, root);// 将头节点入队列
int i = QueueSize(&Q);//i存储队列中数据个数
while(QueueEmpty(&Q))//队列为空说明遍历完毕
{
while (i--)//i中存储了每一层的数据个数,按照i的值进行分层
{
BTNode* Front = QueueFront(&Q);
QueuePop(&Q);//得到队列首元素并出队列
if (Front->_left)//如果队列左子树不为空就继续入队列
QueuePush(&Q, Front->_left);
if (Front->_right)//右不为空就进队列
QueuePush(&Q, Front->_right);
printf("%c ", Front->_data);
}
i = QueueSize(&Q);//i每更新一次,就表明即将出队列的这一层有几个数据
}
QueueDestroy(&Q);//遍历完毕,销毁队列
}
2.5 通过前序遍历生成二叉树
了解了二叉树的相关的遍历之后,我们就可以实现根据数组生成二叉树了,这里依然对代码进行讲解:
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
if (a[*pi] == '#')//如果为'#'就置空
{
(*pi)++;
return NULL;
}
BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
if (tmp == NULL)//其他字符就创造一个节点存储字符
{
perror("malloc fail");
exit(-1);
}
tmp->_data = a[*pi];
(*pi)++;
tmp->_left = BinaryTreeCreate(a,pi );//根节点创造完毕就创造左子树
tmp->_right = BinaryTreeCreate(a, pi);//左子树完了就创造右子树
return tmp;
}
2.6 二叉树叶子节点的个数
叶子节点个数是比较好求得的,只要该节点的左右子节点都为空既是叶子节点,这个比较简单,代码如下:
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);
}
2.7 二叉树第 K 层节点个数
这里我们用递归的思想,他要第K层的节点个数,我们就把第让函数递归K次,等K为1的时候,函数就递归到了第K层,此时就判断他们是否为NULL,为NULL返回0,否则返回 1;这样左右相加最终就得到了第K层有多少节点。参考代码如下:
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(root->_left,k-1) + BinaryTreeLevelKSize(root->_right,k-1);
}
2.8 查找值为X的节点
这个是比较简单的,我们以前序遍历为例,这里用到了三目运算符,参考代码如下:
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->_data == x)
return root;
return BinaryTreeFind(root->_left, x) == NULL ? BinaryTreeFind(root->_right, x) : BinaryTreeFind(root->_left, x);
}
2.9 判断二叉树是否为完全二叉树
在实现这个代码的时候,我们不能以二叉树层数与个数的关系来判断,这样是有问题的。在这里一个正确的方式就是借助队列,我们用层序遍历的方式进行判断,这里我们还是对代码进行说明理解,参考代码如下:
int BinaryTreeComplete(BTNode* root)
{
Queue qu;
BTNode * cur;
int tag = 0;
QueueInit(&qu);
QueuePush(&qu, root);
while (!QueueIsEmpty(&qu))
{
cur = QueueTop(&qu);
putchar(cur->_data);
if (cur->_right && !cur->_left)
{
return 0;//左空右不空一定不是完全二叉树,返回 0
}
if (tag && (cur->_right || cur->_left))
{
return 0;//tag不为1说明前面出现了空节点,此时还有非空节点则一定不是完全二叉树
}
if (cur->_left)
{
QueuePush(&qu, cur->_left);//子节点不为空就入队列
}
if (cur->_right)
{
QueuePush(&qu, cur->_right);
}
else
{
tag = 1;//有的根出现了空节点把tag赋值为1
}
QueuePop(&qu);
}
QueueDestory(&qu);
return 1;//是完全二叉树返回 1
}
3. 汇总:
这是二叉树几个功能的全部代码,函数声明代码在前面,基本功能的地方:
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树 cv
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
if (a[*pi] == '#')
{
(*pi)++;
return NULL;
}
BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
if (tmp == NULL)
{
perror("malloc fail");
exit(-1);
}
tmp->_data = a[*pi];
(*pi)++;
tmp->_left = BinaryTreeCreate(a,pi );
tmp->_right = BinaryTreeCreate(a, pi);
return tmp;
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
if (*root)
{
BinaryTreeDestory(&(*root)->_left);
BinaryTreeDestory(&(*root)->_right);
free(*root);
*root = NULL;
}
else
return;
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (!root)
return 0;
return 1 + BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right);
}
// 二叉树叶子节点个数
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)
{
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(root->_left,k-1) + BinaryTreeLevelKSize(root->_right,k-1);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->_data == x)
return root;
return BinaryTreeFind(root->_left, x) == NULL ? BinaryTreeFind(root->_right, x) : BinaryTreeFind(root->_left, x);
}
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
}
else
{
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root)
{
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
else
printf("N ");
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root)
{
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ",root->_data);
}
else
printf("N ");
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
Queue Q;
QueueInit(&Q);
QueuePush(&Q, root);
int i = QueueSize(&Q);
while(QueueEmpty(&Q))
{
while (i--)
{
BTNode* Front = QueueFront(&Q);
QueuePop(&Q);
if (Front->_left)
QueuePush(&Q, Front->_left);
if (Front->_right)
QueuePush(&Q, Front->_right);
printf("%c ", Front->_data);
}
i = QueueSize(&Q);
}
QueueDestroy(&Q);
}
int BinaryTreeHight(BTNode* root)
{
if (root == NULL)
return 0;
else
{
int i = BinaryTreeHight(root->_left );
int j = BinaryTreeHight(root->_right);
return i > j ? i+1 : j+1;
}
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
Queue qu;
BTNode * cur;
int tag = 0;
QueueInit(&qu);
QueuePush(&qu, root);
while (!QueueIsEmpty(&qu))
{
cur = QueueTop(&qu);
putchar(cur->_data);
if (cur->_right && !cur->_left)
{
return 0;
}
if (tag && (cur->_right || cur->_left))
{
return 0;
}
if (cur->_left)
{
QueuePush(&qu, cur->_left);
}
if (cur->_right)
{
QueuePush(&qu, cur->_right);
}
else
{
tag = 1;
}
QueuePop(&qu);
}
QueueDestory(&qu);
return 1;
}