目录
一,二叉树需要实现的功能
二,下面是各功能详解
0.思想:
1.创建二叉树结点:
2.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
3.二叉树销毁:
4.前序遍历:
5.中序遍历:
6.后序遍历:
7.层序遍历:
1.先实现队列的基本功能:
2.基于队列实现层序:
8.计算各类结点数量:
1.计算二叉树结点数量:
2.计算叶子结点数量:
3.计算K层结点数量:
9.二叉树查找值为X的结点:
10.判断二叉树是否为完全二叉树:
一,二叉树需要实现的功能
// 通过前序遍历的数组"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);
二,下面是各功能详解
0.思想:
下面很多功能都涉及分治的思想(分治法是算法常用的解题方法之一,是将一个大的问题拆分为若干小的问题。)
1.创建二叉树结点:
//重命名存储变量类型,方便更改
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
2.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi) {
if (*pi >= n || a[*pi] == '#') {
(*pi)++;
return NULL;
}
BTNode* Node = (BTNode*)malloc(sizeof(BTNode));
if (Node == NULL) {
perror("BinaryTreeCreate::malloc");
exit(0);
}
Node->_data = a[*pi];
(*pi)++;
Node->_left = BinaryTreeCreate(a, n, pi);
Node->_right = BinaryTreeCreate(a, n, pi);
return Node;
}
按照以上所给数组描述,我们创建出的二叉树:
3.二叉树销毁:
二叉树的存储类似链表,可以由前面的结点找到后面的结点,因此二叉树的销毁也是由后向前销毁会方便很多,所以我们采取后序来销毁二叉树
// 二叉树销毁
void BinaryTreeDestory(BTNode** root) {
if (*root == NULL) {
return;
}
BinaryTreeDestory(&(*root)->_left);
BinaryTreeDestory(&(*root)->_right);
free(*root);
*root = NULL;
}
4.前序遍历:
i、先访问根结点;
ii、再前序遍历左子树;
iii、最后前序遍历右子树;
算法实现:
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root) {
if (root != NULL) {
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
}
5.中序遍历:
i、中序遍历左子树;
ii、访问根结点;
iii、中序遍历右子树
算法实现:
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root) {
if (root != NULL) {
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
}
6.后序遍历:
i、后序遍历左子树
ii、后序遍历右子树
iii、访问根结点
算法实现:
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root) {
if (root != NULL) {
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
}
7.层序遍历:
设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
思路:层序遍历需要用到队列的知识,就是先将根结点入队,判断队列是否为空,循环将队首元素出队的同时队首元素子节结点入队
算法实现:
1.先实现队列的基本功能:
typedef BTNode QDataType;
// 链式结构:表示队列
typedef struct QListNode
{
struct QListNode* _next;
QDataType* _data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* _front;
QNode* _rear;
}Queue;
// 初始化队列
void QueueInit(Queue* q) {
assert(q);
q->_front = NULL;
q->_rear = NULL;
}
int QueueEmpty(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType* data) {
assert(q);
QNode* tmp = (QNode*)malloc(sizeof(QNode));
if (tmp == NULL) {
perror("QueuePush:malloc");
return;
}
tmp->_data = data;
tmp->_next = NULL;
if (QueueEmpty(q)) {
q->_front = tmp;
q->_rear = tmp;
}
q->_rear->_next = tmp;
q->_rear = tmp;
}
// 队头出队列
void QueuePop(Queue* q) {
if (QueueEmpty(q)) {
printf("Pop: Queue is empty\n"); // 更清晰的错误信息
exit(0);
}
q->_front = q->_front->_next;
//free(tmp);
}
// 获取队列头部元素
QDataType* QueueFront(Queue* q) {
return q->_front->_data;
}
// 获取队列队尾元素
QDataType* QueueBack(Queue* q) {
return q->_rear->_data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q) {
QNode* cur = q->_front;
int size = 0;
while (cur) {
size++;
cur = cur->_next;
}
return size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q) {
assert(q);
if (q->_front == NULL) {
return 1;
}
return 0;
}
// 销毁队列
void QueueDestroy(Queue* q) {
assert(q);
while (!QueueEmpty(q)) {
QueuePop(q);
}
}
2.基于队列实现层序:
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
Queue* queue = (Queue*)malloc(sizeof(Queue));
BTNode* front;
QueueInit(queue);
if (root) {
QueuePush(queue, root);
}
while (!QueueEmpty(queue)) {
front = QueueFront(queue);
if (front->_left)
{
QueuePush(queue, front->_left);
}
if (front->_right)
{
QueuePush(queue, front->_right);
}
printf("%c ",front->_data);
QueuePop(queue);
}
printf("\n");
QueueDestroy(queue);
}
8.计算各类结点数量:
1.计算二叉树结点数量:
// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
if (root == NULL) {
return 0;
}
else {
return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}
}
2.计算叶子结点数量:
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root) {
if (root == NULL) {
return 0;
}
if (root->_left == NULL && root->_right == NULL) {
return 1;
}
else {
return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
}
3.计算K层结点数量:
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {
if (root == NULL) {
return 0;
}
if (k == 1) {
return 1;
}
return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
9.二叉树查找值为X的结点:
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
if (root==NULL) {
return NULL;
}
if (root->_data == x) {
return root;
}
BTNode* r1 = BinaryTreeFind(root->_left, x);
if (r1 != NULL) {
return r1;
}
return BinaryTreeFind(root->_right, x);
}
10.判断二叉树是否为完全二叉树:
根据完全二叉树的定义,具有n个结点的完全二叉树与满二叉树中编号从1~n的结点一一对应。
算法思想:采用层次遍历算法,将所有结点加入队列(包括空结点)。遇到空结点时,查看其后是否有非空结点。若有,则二叉树不是完全二叉树。
判断二叉树是否为完全二叉树是二叉树层序遍历的基本用途之一,也要借助队列来实现;
算法实现:
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {
Queue queue;
QueueInit(&queue);
if (root) {
QueuePush(&queue, root);
}
while (!QueueEmpty(&queue)) {
BTNode* front = QueueFront(&queue);
QueuePop(&queue);
if (front == NULL) {
break;
}
QueuePush(&queue, front->_left);
QueuePush(&queue, front->_right);
}
while (!QueueEmpty(&queue)) {
BTNode* front = QueueFront(&queue);
QueuePop(&queue);
if (front) {
QueueDestroy(&queue);
return 0;
}
}
QueueDestroy(&queue);
return 1;
}