目录
前言
上一章我们讲了二叉树的顺序存储的结构,接下来本章会将二叉树链式存储的结构和功能全部讲解完,这样二叉树的章节就完结啦,希望大家能学会以及理解喔。
一、链式存储结构
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。那么链式的二叉树的结构体是什么样子的呢?具体代码如下:
typedef char BTDatatype;
typedef struct BinaryTreeNode {
BTDatatype val;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
之前我们就讲了二叉树是由左右孩子节点结合而成的一颗二叉树,所有在创建结构体时,我们要准备好两个节点,方便连接起来。
二、链式二叉树的基本功能
1.通过前序建立一个链式二叉树
我们创建好之后就要建立一个二叉树了,那么该如何建立呢?这里需要先学习了前序遍历才能理解前序建立二叉树,这里就大概讲解,我们有一个前序遍历然后生成一颗二叉树,节点内容用字母表示,空节点用 # 表示:
char arr[] = "ABD##E#H##CF##G##";
准备好一个数组之后,就开始遍历生成了,具体步骤因为需要遍历,所有我们要想好终止条件,1.当 arr 数组遍历完后或为空时我们就要返回结束该遍历,2.当遇到 # 时,表示该节点为空节点我们要返回空,继续后面的代码,具体如下:
BTNode* BinaryTreeCreate(BTDatatype* arr, int arr_size, int* start) {
if (arr == NULL || arr_size <= 0) {
return;
}
if (arr[*start] == '#') {
(*start)++;
return NULL;
}
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
assert(newnode);
newnode->val = arr[*start];
(*start)++;
newnode->left = BinaryTreeCreate(arr, arr_size, start);
newnode->right = BinaryTreeCreate(arr, arr_size, start);
return newnode;
}
这里的 *start 是用来控制数组的插入位置,因为如果在函数里创建一个变量控制数组位置,进入下一个函数时,虽然变量带进去了也发生改变了,但是当他返回时,变量的数据是上一个空间的数据,不符合我们的需求,所以得使用外部传进来的 *start 进行改变。
2.二叉树前中后序遍历
二叉树的遍历分别由前中后序,每种遍历都有不同的要求,下面会给大家讲解三个不同的遍历。
2.1前序遍历
首先是前序遍历,也叫先序遍历,该遍历是中左右的顺序进行遍历,什么意思呢?就是头节点->左节点->右节点的顺序进行遍历生成:
void BinaryTreePrevOrder(BTNode* pheap) {
if (!pheap) {
printf("# ");
return;
}
printf("%c ", pheap->val);
BinaryTreePrevOrder(pheap->left);
BinaryTreePrevOrder(pheap->right);
}
2.2中序遍历
中序遍历跟前序遍历的思路大致一样,区别在于是左中右的循序遍历:
void BinaryTreeInOrder(BTNode* pheap) {
if (!pheap) {
printf("# ");
return;
}
BinaryTreeInOrder(pheap->left);
printf("%c ", pheap->val);
BinaryTreeInOrder(pheap->right);
}
2.3后序遍历
后序遍历的顺序是左右中循序遍历:
void BinaryTreePostOrder(BTNode* pheap) {
if (!pheap) {
printf("# ");
return;
}
BinaryTreePostOrder(pheap->left);
BinaryTreePostOrder(pheap->right);
printf("%c ", pheap->val);
}
三个遍历会发现,只是打印的位置有些调整就发生了三种不同的遍历顺序,所以要控制好代码的位置,不然就实现不出想要的功能了。
3.求二叉树的节点个数
求二叉树的节点个数方式很多,这是其中一种快捷的方法,还是要思考一下,就能理解了。
int BinaryTreeSize(BTNode* pheap) {
if (pheap == NULL)
return 0;
return BinaryTreeSize(pheap->left) + BinaryTreeSize(pheap->right) + 1;
}
4.求二叉树叶子节点个数
求二叉树的叶子节点个数就稍微要转变一下了,要当左节点为空右节点也为空时,才能说明该节点是空节点,才能返回1回去。
int BinaryTreeLeafSize(BTNode* pheap) {
if (pheap == NULL)
return 0;
if (!pheap->left && !pheap->right)
return 1;
return BinaryTreeLeafSize(pheap->left) + BinaryTreeLeafSize(pheap->right);
}
5.求二叉树第k层节点个数
求二叉树第k层的节点个数,就需要想到第k层的最后一层就是我们需要的节点个数,那么只需要将k的数减到k为1时,就是第k层的节点个数了,具体代码如下:
int BinaryTreeLevelKSize(BTNode* pheap, int k) {
assert(k > 0);
if (pheap == NULL)
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(pheap->left, k - 1) + BinaryTreeLevelKSize(pheap->right, k - 1);
}
6.求二叉树的高度
求二叉树的高度时,我们需要判断一下,左子树高还是右子树高,哪个子树高我们就返回哪个子树的高度,这里要记得 +1 因为也要算是根节点。
int BinaryTreeHeight(BTNode* pheap) {
int left_h = 0, right_h = 0;
if (pheap == NULL)
return 0;
left_h = BinaryTreeHeight(pheap->left);
right_h = BinaryTreeHeight(pheap->right);
return left_h > right_h ? left_h + 1 : right_h + 1;
}
7.二叉树的层序遍历
我们需要求二叉树的层序遍历的时候我们要用是 栈或者队列去实现这功能,这里我用的是队列去实现,具体步骤思路是,先将跟节点 push 进去,当队列不为空时,我们创建一个节点用于保存即将pop的节点,在将保存的节点的左右孩子节点都push进队列,重复以上步骤即可。
void BinaryTreeLevelOrder(BTNode* root) {
Queue p;
QListInit(&p);
printf("\n");
if (root)
QListPush(&p, root);
int LeaveSize = 1;
while (!(QueueEmpty(&p))) {
while (LeaveSize--) {
BTNode* front = QueueFront(&p);
QListPop(&p);
printf("%c ", front->val);
if (front->left)
QListPush(&p, front->left);
if (front->right)
QListPush(&p, front->right);
}
printf("\n");
LeaveSize = QueueSize(&p);
}
QListDestory(&p);
}
8.判断二叉树是否是完全二叉树
当我们要判断一颗二叉树是否为完全二叉树时,我们可以用上刚刚的层序遍历去实现,当传进了一个NULL节点,但是后面还有元素,说明该树不是完全二叉树了,这样就可以完成判断了。
bool BinaryTreeComplete(BTNode* root) {
Queue p;
QListInit(&p);
printf("\n");
if (root)
QListPush(&p, root);
while (!(QueueEmpty(&p))) {
BTNode* front = QueueFront(&p);
QListPop(&p);
if (front == NULL)
break;
QListPush(&p, front->left);
QListPush(&p, front->right);
}
while (!QueueEmpty(&p)) {
BTNode* front = QueueFront(&p);
QListPop(&p);
if (front) {
QListDestory(&p);
return false;
}
}
QListDestory(&p);
return true;
}
三、源代码
BinaryTree.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef char BTDatatype;
typedef struct BinaryTreeNode {
BTDatatype val;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
//通过前序遍历数组数据生成二叉树
BTNode* BinaryTreeCreate(BTDatatype* arr, int arr_size ,int *p);
//二叉树节点个数
int BinaryTreeSize(BTNode* pheap);
//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* pheap);
//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* pheap, int k);
//二叉树的高度
int BinaryTreeHeight(BTNode* pheap);
//查找值为x的节点
BTNode* BinaryTreeFindNode(BTNode* pheap, BTDatatype x);
//二叉树前序遍历
void BinaryTreePrevOrder(BTNode* pheap);
//二叉树中序遍历
void BinaryTreeInOrder(BTNode* pheap);
//二叉树后序遍历
void BinaryTreePostOrder(BTNode* pheap);
//层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);
//销毁二叉树
void BinaryTreeDestory(BTNode** pheap);
BinaryTree.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
#include "BinaryTree.h"
//通过前序遍历数据生成二叉树
BTNode* BinaryTreeCreate(BTDatatype* arr, int arr_size, int* start) {
if (arr == NULL || arr_size <= 0) {
return;
}
if (arr[*start] == '#') {
(*start)++;
return NULL;
}
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
assert(newnode);
newnode->val = arr[*start];
(*start)++;
newnode->left = BinaryTreeCreate(arr, arr_size, start);
newnode->right = BinaryTreeCreate(arr, arr_size, start);
return newnode;
}
//二叉树节点个数
int BinaryTreeSize(BTNode* pheap) {
if (pheap == NULL)
return 0;
return BinaryTreeSize(pheap->left) + BinaryTreeSize(pheap->right) + 1;
}
//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* pheap) {
if (pheap == NULL)
return 0;
if (!pheap->left && !pheap->right)
return 1;
return BinaryTreeLeafSize(pheap->left) + BinaryTreeLeafSize(pheap->right);
}
//二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* pheap, int k) {
assert(k > 0);
if (pheap == NULL)
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(pheap->left, k - 1) + BinaryTreeLevelKSize(pheap->right, k - 1);
}
//二叉树的高度
int BinaryTreeHeight(BTNode* pheap) {
int left_h = 0, right_h = 0;
if (pheap == NULL)
return 0;
left_h = BinaryTreeHeight(pheap->left);
right_h = BinaryTreeHeight(pheap->right);
return left_h > right_h ? left_h + 1 : right_h + 1;
}
//查找值为x的节点
BTNode* BinaryTreeFindNode(BTNode* pheap, BTDatatype x) {
BTNode* p = NULL;
if (pheap == NULL)
return NULL;
if (pheap->val == x)
return pheap;
p = BinaryTreeFindNode(pheap -> left, x);
if (p != NULL)
return p;
p = BinaryTreeFindNode(pheap->right, x);
if (p != NULL)
return p;
}
//二叉树前序遍历
void BinaryTreePrevOrder(BTNode* pheap) {
if (!pheap) {
printf("# ");
return;
}
printf("%c ", pheap->val);
BinaryTreePrevOrder(pheap->left);
BinaryTreePrevOrder(pheap->right);
}
//二叉树中序遍历
void BinaryTreeInOrder(BTNode* pheap) {
if (!pheap) {
printf("# ");
return;
}
BinaryTreeInOrder(pheap->left);
printf("%c ", pheap->val);
BinaryTreeInOrder(pheap->right);
}
//二叉树后序遍历
void BinaryTreePostOrder(BTNode* pheap) {
if (!pheap) {
printf("# ");
return;
}
BinaryTreePostOrder(pheap->left);
BinaryTreePostOrder(pheap->right);
printf("%c ", pheap->val);
}
//层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
Queue p;
QListInit(&p);
printf("\n");
if (root)
QListPush(&p, root);
int LeaveSize = 1;
while (!(QueueEmpty(&p))) {
while (LeaveSize--) {
BTNode* front = QueueFront(&p);
QListPop(&p);
printf("%c ", front->val);
if (front->left)
QListPush(&p, front->left);
if (front->right)
QListPush(&p, front->right);
}
printf("\n");
LeaveSize = QueueSize(&p);
}
QListDestory(&p);
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root) {
Queue p;
QListInit(&p);
printf("\n");
if (root)
QListPush(&p, root);
while (!(QueueEmpty(&p))) {
BTNode* front = QueueFront(&p);
QListPop(&p);
if (front == NULL)
break;
QListPush(&p, front->left);
QListPush(&p, front->right);
}
while (!QueueEmpty(&p)) {
BTNode* front = QueueFront(&p);
QListPop(&p);
if (front) {
QListDestory(&p);
return false;
}
}
QListDestory(&p);
return true;
}
//销毁二叉树
void BinaryTreeDestory(BTNode** pheap) {
if ((*pheap) == NULL) {
return;
}
BinaryTreeDestory(&(*pheap)->left);
BinaryTreeDestory(&(*pheap)->right);
free(*pheap);
*pheap = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
#include "BinaryTree.h"
int main() {
char arr[] = "ABD##E#H##CF##G##";
int start = 0;
BTNode* phead = BinaryTreeCreate(arr, sizeof(arr) / sizeof(arr[0]),&start);
printf("TreeSize = %d\n", BinaryTreeSize(phead));
printf("TreeLeafSize = %d\n", BinaryTreeLeafSize(phead));
printf("TreeLevelKSize = %d\n", BinaryTreeLevelKSize(phead, 2));
printf("TreeHeight = %d\n", BinaryTreeHeight(phead));
BTNode* FineNode = BinaryTreeFindNode(phead, 'C');
assert(FineNode);
printf("FineNode : %p \n", FineNode);
printf("TreePrevOrder:");
BinaryTreePrevOrder(phead);
printf("\n");
printf("TreeINOrder:");
BinaryTreeInOrder(phead);
printf("\n");
printf("TreePostOrder:");
BinaryTreePostOrder(phead);
printf("\n");
printf("TreeLevelOrder:");
BinaryTreeLevelOrder(phead);
printf("TreeComplete:%d", BinaryTreeComplete(phead));
printf("\n");
BinaryTreeDestory(&phead);
}
总结
本章数据结构二叉树的内容已经完结了,下一章会讲到数据结构的各种排序,以及排序的基础概念,感谢大家看到这里喔。