目录
链式二叉树是实现二叉树的一种常规方法,普通链式二叉树虽然没有现实意义,但是自己写一次对于初学者理解二叉树有较大的帮助,这里我就讲讲链式二叉树的一些重要函数的实现。
1.二叉树的结构体结点
二叉树的结构体十分简单,只有三部分,根结点和其左右孩子
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
这个很简单,我就不多讲了。
2.二叉树的创建
二叉树的创建有一种比较原始的方法就是自己手搓一个,这种方法很费劲,但是在测试代码时有一定的作用,如下:
BTNode* p1 = (BTNode*)malloc(sizeof(BTNode));
BTNode* p2 = (BTNode*)malloc(sizeof(BTNode));
BTNode* p3 = (BTNode*)malloc(sizeof(BTNode));
BTNode* p4 = (BTNode*)malloc(sizeof(BTNode));
BTNode* p5 = (BTNode*)malloc(sizeof(BTNode));
BTNode* p6 = (BTNode*)malloc(sizeof(BTNode));
p1->_data = 1;
p1->_left = p2;
p1->_right = p3;
p2->_data = 2;
p2->_left = p4;
p2->_right = p5;
p3->_data = 3;
p3->_left = p6;
p3->_right = NULL;
p4->_data = 4;
p4->_left = NULL;
p4->_right = NULL;
p5->_data = 5;
p5->_left = NULL;
p5->_right = NULL;
p6->_data = 6;
p6->_left = NULL;
p6->_right = NULL;
这里创建了如下图所示的二叉树
当然,正常使用我们显然不能使用这种方法,我采用的是通过其前序遍历生成的数组反向生成树的方法,代码如下:
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
if (a[(*pi)] == '#')
{
(*pi)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
if (!root)
{
perror("malloc fail");
exit(0);
}
root->_data = a[(*pi)++];//给根节点赋值
root->_left=BinaryTreeCreate(a, n, pi);
//给左孩子赋值
root->_right=BinaryTreeCreate(a, n, pi);
//给右孩子赋值
return root;
}
此处函数的参数的意义分别为:BTDataType* a // 前序遍历得到的数组
int n //数组的大小(此函数中没什么作用)
int* pi //此时赋值给结点的数组元素的索引
看到这里,可能有人会疑惑,为什么要用 int *pi,不直接使用 int pi,这是因为这是一个递归函数,倘若我们使用一个int 值,对于每次需要使用的索引不能够判断,但是使用 int *pi,每次使用完我们直接修改其本身值,下次调用就直接是修改后的值。
3.二叉树的遍历
3.1.前中后序遍历
二叉树的前序,中序,后序遍历十分简单,无非是一种简单的递归,他们都是二叉树深度优先遍历(DFS)的一种,代码如下:
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
3.2.层序遍历
层序遍历是链式二叉树较难的一种遍历方式,难点就是层序遍历需要把二叉树和队列结合起来,规则就是:根节点出队列,此根节点的左右孩子就入队列。并且,链式二叉树的层序遍历是二叉树的广度优先遍历(BFS)的一种。代码如下:
//层序遍历我选择使用数组模拟循环队列的方法
//循环链表结构体
#define CQUEUESIZE 12//如果需要修改循环队列大小,直接修改CQUEUESIZE的define
struct CycleQueue
{
int front;
int rear;
BTNode* elem[CQUEUESIZE];
};//循环队列
//入队列
void Push(CycleQueue* node, BTNode* x)
{
//之所以这里没有排除node==NULL的情况,是因为在判断完全二叉树功能时有作用
if ((node->rear + 1) % CQUEUESIZE == node->front)
{
printf("循环队列已满\n");
exit(0);
}
node->elem[node->rear] = x;
node->rear = (node->rear + 1) % CQUEUESIZE;
}
//判断队列是否为空
bool Empty(CycleQueue* node)
{
assert(node);
if (node->front == node->rear)
{
return true;
}
return false;
}
//出队列
void Pop(CycleQueue* node)
{
if (Empty(node))
{
return;
}
node->front = (node->front + 1) % CQUEUESIZE;
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
CycleQueue* CQueue = (CycleQueue*)malloc(sizeof(CycleQueue));
if (!CQueue)
{
perror("malloc fail");
exit(0);
}
CQueue->front = 0;
CQueue->rear = 0;
BTNode *p=root;
if(root!=NULL)
Push(CQueue, p);
while (!Empty(CQueue))
{
printf("%c ", p->_data);
if (p->_left)
Push(CQueue, p->_left);
if (p->_right)
Push(CQueue, p->_right);
Pop(CQueue);
if (!Empty(CQueue))
p= CQueue->elem[CQueue->front];
}
free(CQueue);
CQueue = NULL;
}
若大家对于循环队列有疑惑,可以看我的另一篇博客
4.判断是否为完全二叉树
完全二叉树是一种特殊的树,除了最后两层可能有叶子结点,其他层不会有叶子结点,在层序遍历中,它也具有特殊性,如下图
按层序遍历为 :1 2 3 4 5 6 NULL NULL NULL NULL NULL NULL NULL
我们可以发现,在层序遍历中,如果出现NULL,接下来都是NULL,也就是是NULL是连续到结束的,这也是入队列我不排除入队列元素为NULL的原因。
代码如下:
int BinaryTreeComplete(BTNode* root)
{
if (root == NULL)//如果树为NULL
{
return 1;
}
CycleQueue* CQueue = (CycleQueue*)malloc(sizeof(CycleQueue));
if (!CQueue)
{
perror("malloc fail");
exit(0);
}
CQueue->front = 0;
CQueue->rear = 0;
if (root != NULL)
Push(CQueue, root);
while (!Empty(CQueue))
{
BTNode* p = CQueue->elem[CQueue->front];
if (p)//如果队列头不是NULL,入其左右孩子
{
Push(CQueue, p->_left);
Push(CQueue, p->_right);
Pop(CQueue);
}
else//如果队列头为NULL,判断NULL是否是连续的
{
while (!Empty(CQueue))
{
BTNode* flag = CQueue->elem[CQueue->front];
if (flag)//如果出现不是NULL的指针,说明NULL不连续,不是完全二叉树
{
return 0;
}
Pop(CQueue);
}
//while结束,说明NULL是连续到结束的
return 1;
}
}
//使用完动态开辟的空间不要忘了free
free(CQueue);
CQueue = NULL;
}
其他函数都是使用递归的方法解决,比较简单,我就不一一说明,大家可以自行查看注释。
5.完整代码
//BinaryTree.h 文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#define CQUEUESIZE 12
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
struct CycleQueue
{
int front;
int rear;
BTNode* elem[CQUEUESIZE];
};//循环队列
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, 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);
//循环队列插入函数
void Push(CycleQueue* node, BTNode* x);
//判断循环队列是否为空
bool Empty(CycleQueue* node);
//循环队列删除函数
void Pop(CycleQueue* node);
//BinaryTree.cpp 文件
#include"BinaryTree.h"
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
if (a[(*pi)] == '#')
{
(*pi)++;
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
if (!root)
{
perror("malloc fail");
exit(0);
}
root->_data = a[(*pi)++];
root->_left=BinaryTreeCreate(a, n, pi);
root->_right=BinaryTreeCreate(a, n, pi);
return root;
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
if (*root == NULL)
{
return;
}
if((*root)->_left)
BinaryTreeDestory(&((*root)->_left));
if((*root)->_right)
BinaryTreeDestory(&((*root)->_right));
free(*root);
*root = NULL;
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return BinaryTreeSize(root->_left) +
BinaryTreeSize(root->_right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->_left == root->_right && root->_left == NULL)
{
return 1;
}
return BinaryTreeLeafSize(root->_left) +
BinaryTreeLeafSize(root->_right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
int leftK = BinaryTreeLevelKSize(root->_left, k - 1);
int rightK = BinaryTreeLevelKSize(root->_right, k - 1);
return leftK + rightK;
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
BTNode* left = BinaryTreeFind(root->_left, x);
if (left)
return left;
BTNode* right = BinaryTreeFind(root->_right, x);
if (right)
return right;
return NULL;
}
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
//层序遍历我选择使用数组模拟循环队列的方法
void Push(CycleQueue* node, BTNode* x)
{
if ((node->rear + 1) % CQUEUESIZE == node->front)
{
printf("循环队列已满\n");
exit(0);
}
node->elem[node->rear] = x;
node->rear = (node->rear + 1) % CQUEUESIZE;
}
bool Empty(CycleQueue* node)
{
assert(node);
if (node->front == node->rear)
{
return true;
}
return false;
}
void Pop(CycleQueue* node)
{
if (Empty(node))
{
return;
}
node->front = (node->front + 1) % CQUEUESIZE;
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
int x = 1;
if (root == NULL)
{
return 1;
}
CycleQueue* CQueue = (CycleQueue*)malloc(sizeof(CycleQueue));
if (!CQueue)
{
perror("malloc fail");
exit(0);
}
CQueue->front = 0;
CQueue->rear = 0;
if (root != NULL)
Push(CQueue, root);
while (!Empty(CQueue))
{
BTNode* p = CQueue->elem[CQueue->front];
if (p)
{
Push(CQueue, p->_left);
Push(CQueue, p->_right);
Pop(CQueue);
}
else
{
while (!Empty(CQueue))
{
BTNode* flag = CQueue->elem[CQueue->front];
if (flag)
{
return 0;
}
Pop(CQueue);
}
return 1;
}
}
free(CQueue);
CQueue = NULL;
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
if (root == NULL)
{
return;
}
CycleQueue* CQueue = (CycleQueue*)malloc(sizeof(CycleQueue));
if (!CQueue)
{
perror("malloc fail");
exit(0);
}
CQueue->front = 0;
CQueue->rear = 0;
BTNode *p=root;
if(root!=NULL)
Push(CQueue, p);
while (!Empty(CQueue))
{
printf("%c ", p->_data);
if (p->_left)
Push(CQueue, p->_left);
if (p->_right)
Push(CQueue, p->_right);
Pop(CQueue);
if (!Empty(CQueue))
p= CQueue->elem[CQueue->front];
}
free(CQueue);
CQueue = NULL;
}
int main()
{
char string[] = "ABD##E#H##CF##G##";
int n = strlen(string);
int p = 0;
int* pi = &p;
BTNode* root = BinaryTreeCreate(string, n, pi);
printf("前序遍历为:\n");
BinaryTreePrevOrder(root);
printf("\n中序遍历为:\n");
BinaryTreeInOrder(root);
printf("\n后序遍历为:\n");
BinaryTreePostOrder(root);
printf("\n层序遍历为:\n");
BinaryTreeLevelOrder(root);
printf("\n二叉树结点个数为 :%d\n", BinaryTreeSize(root));
printf("叶子结点数为:%d\n", BinaryTreeLeafSize(root));
printf("二叉树第1层结点个数为 :%d\n", BinaryTreeLevelKSize(root, 1));
printf("二叉树第2层结点个数为 :%d\n", BinaryTreeLevelKSize(root, 2));
printf("二叉树第3层结点个数为 :%d\n", BinaryTreeLevelKSize(root, 3));
printf("查找值为G的结点,G的地址为:%p\n", BinaryTreeFind(root, 'G'));
int i = BinaryTreeComplete(root);
if (i)
{
printf("此树是完全二叉树\n");
}
else
{
printf("此树不是完全二叉树\n");
}
BinaryTreeDestory(&root);
printf("\n二叉树销毁成功\n");
return 0;
}