本期只讲解部分接口,相当于对之前内容的一些补充,需要一定基础,建议先看之前的文章后再看本期
(1条消息) 树和二叉树-CSDN博客
目录
树的相关概念
二叉树的结构
二叉树的前、中、后序遍历
二叉树的节点个数
二叉树的深度
二叉树第k层节点个数
二叉树查找值为x的节点
层序遍历
判断二叉树是否是完全二叉树
二叉树销毁
节点的度 :一个节点含有的子树的个数称为该节点的度; 如上图: A 的为 6叶节点或终端节点 :度为 0 的节点称为叶节点; 如上图: B 、 C 、 H 、 I... 等节点为叶节点非终端节点或分支节点 :度不为 0 的节点; 如上图: D 、 E 、 F 、 G... 等节点为分支节点双亲节点或父节点 :若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图: A 是 B 的父节点孩子节点或子节点 :一个节点含有的子树的根节点称为该节点的子节点; 如上图: B 是 A 的孩子节点兄弟节点 :具有相同父节点的节点互称为兄弟节点; 如上图: B 、 C 是兄弟节点树的度 :一棵树中,最大的节点的度称为树的度; 如上图:树的度为 6节点的层次 :从根开始定义起,根为第 1 层,根的子节点为第 2 层,以此类推;树的高度或深度 :树中节点的最大层次; 如上图:树的高度为 4堂兄弟节点 :双亲在同一层的节点互为堂兄弟;如上图: H 、 I 互为兄弟节点节点的祖先 :从根到该节点所经分支上的所有节点;如上图: A 是所有节点的祖先子孙 :以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是 A 的子孙森林 :由 m ( m>0 )棵互不相交的树的集合称为森林;
这些内容以及其他的相关知识都在之前的文章里介绍过了,这里再简单提一下,之后的相关概念不再赘述,我们直接进入正题
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
二叉树可以被分为3个部分,根、左子树、右子树,所以他的结构也是如此,我们需要左右子树的指针
二叉树的每一个节点,我们都应该分为3部分来看待,即根、左子树和右子树,并且二叉树的叶子节点我们也应该这样看待,只是叶子节点的左右子树为空
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root) {
assert(root);
if (root == NULL) {
printf("NULL ");
return;
}
printf("%d ", root->data);
BinaryTreePrevOrder(root->left);
BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root) {
assert(root);
if (root == NULL) {
printf("NULL ");
return;
}
BinaryTreeInOrder(root->left);
printf("%d ", root->data);
BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root) {
assert(root);
if (root == NULL) {
printf("NULL ");
return;
}
BinaryTreePostOrder(root->left);
BinaryTreePostOrder(root->right);
printf("%d ", root->data);
}
前序遍历也叫先根遍历,中序遍历叫做中根遍历,后序叫后根,意思是访问根节点的顺序,比如前序遍历,我们要先访问根节点,即我们这里打印出根节点的元素,然后再去访问左子树,然后去访问右子树,中序是先访问左子树,接着是访问根节点,最后访问右子树,后序为左子树、右子树、根的顺序,这里我们将空也打印出来,因为空也属于二叉树的一部分
// 二叉树节点个数
int BinaryTreeSize(BTNode* root) {
assert(root);
if (root == NULL) {
return 0;
}
int left = BinaryTreeSize(root->left);
int right = BinaryTreeSize(root->right);
return left + right + 1;
}
我们分别计算左右子树的节点树,遇到空返回0,接着我们将左右子树的节点个数加起来然后再加1,即加上这个节点自己,我们使用递归的思想即可完成,这段代码也可以使用一个三目运算符来统一起来
int maxDepth(struct TreeNode* root){
if(root==NULL){
return 0;
}
int leftDepth=maxDepth(root->left);
int rightDepth=maxDepth(root->right);
return leftDepth>rightDepth?leftDepth+1:rightDepth+1;
}
与计算节点个数类似,不过这段代码不推荐用三目运算符来实现,因为我们在第一次比较两个子树高度后,?后面的语句+1时还会再次进行计算,即重复运行代码,效率太低, 所以我们先将结果保存起来,然后比较
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k) {
assert(k > 0);
assert(root);
if (root == NULL) {
return 0;
}
if (k == 1) {
return 1;
}
int leftnum = BinaryTreeLevelKSize(root->left, k - 1);
int rightnum = BinaryTreeLevelKSize(root->right, k - 1);
return leftnum + rightnum;
}
第k层的节点个数等于左子树的第k-1层节点数+右子树的第k-1层节点数
这里的判断条件除了为空时要判断,k==1时也要判断并返回,因为为1时的那层就是我们需要的那层(根节点为第1层)
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
assert(root);
if (root == NULL) {
return NULL;
}
if (root->data == x) {
return root;
}
BTNode* left = BinaryTreeFind(root->left,x);
BTNode* right = BinaryTreeFind(root->right, x);
if (left != NULL) {
return left;
}
else {
return right;
}
}
我们寻找值为x的节点,同样的使用递归来找,找到了就层层返回,找不到返回NULL,最后两句代码还可以更细节一点,比如加上如果右不为空返回右,最后再返回空也是可以的,但差别不大
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
assert(root);
Queue queue;
QueueInit(&queue);
if(root!=NULL)
QueuePush(&queue,root);
while (!QueueEmpty(&queue)) {
BTNode* tmp = QueueFront(&queue);
QueuePop(&queue);
printf("%d ", tmp->data);
if (tmp->left) {
QueuePush(&queue, tmp->left);
}
if (tmp->right) {
QueuePush(&queue, tmp->right);
}
}
QueueDestroy(&queue);
}
层序遍历是需要使用到队列的,写队列的文章我给大家附上链接
(1条消息) 栈和队列-----重制版_KLZUQ的博客-CSDN博客
层序遍历即为一层一层的遍历,思路为我们使用队列先将根节点入队列,接着将根节点的出队列,出的同时将他的左右子树都入队列,以此类推,我们就可以进行层序遍历了,最后不要忘记释放队列的空间
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root) {
Queue queue;
if (root != NULL)
QueuePush(&queue, root);
while (!QueueEmpty(&queue)) {
BTNode* tmp = QueueFront(&queue);
QueuePop(&queue);
if (tmp == NULL) {
break;
}
else {
QueuePush(&queue, root->left);
QueuePush(&queue, root->right);
}
}
//判断是否为完全二叉树,此时队列里应该都为空
while (!QueueEmpty(&queue)) {
BTNode* tmp = QueueFront(&queue);
QueuePop(&queue);
if (tmp) {//遇到非空节点,说明不是完全二叉树
QueueDestroy(&queue);
return false;
}
}
QueueDestroy(&queue);
return true;
}
我们继续使用队列来帮助我们完成该函数,如果一棵树为完全二叉树,我们使用层序遍历,将他所有节点都入队列,在我们第一次遇到空后,后续节点不会出现非空节点,否则就不是完全二叉树了
// 二叉树销毁
void BinaryTreeDestory(BTNode** root) {
assert(root);
if (root == NULL) {
return;
}
BinaryTreeDestory((*root)->left);
BinaryTreeDestory((*root)->right);
free(root);
}
二叉树的销毁我们最好选择后序遍历的顺序,最后销毁根节点,不然我们还需要对左右子树先进行存储再销毁,会变得麻烦起来
以上即为本期全部内容,希望大家可以有所收获
如有错误,还请指正