网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
2.2.1 前序遍历的实现
代码实现:
void PreOrder(BTNode*root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);//遍历根节点
PreOrder(root->left);//遍历左子树节点
PreOrder(root->right);//遍历右子树节点
}
图示:()中的数字为代码执行顺序
2.2.2 中序遍历的实现
代码实现:
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
图示:
2.2.3 后序遍历的实现
代码实现:
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
InOrder(root->right);
printf("%d ", root->data);
}
2.3 层序遍历(广度优先遍历(BFS))
层序遍历——直接一层一层的进行遍历即可,从头节点开始逐个向后进行遍历。上图中的遍历顺序为:1—2—4—3—5—6
思路:
- 先把根入队列,借助队列先进先出的性质
- 上一层的节点出的时候,带下一层的左右子节点进去
图示:
实现:
注意:使用Queue.c和Queue.h两个文件,同时把队列中存储的数据类型定义为BinaryTreeNode*,注意头文件的包含问题和在Queue文件中的结构体的声明!
Queue.h文件中的修改:
Test.c文件中的修改
代码:
void LevelOrder(BTNode* root)
{
Queue q;//队列的创建
QueueInit(&q);
if (root)//判断是否为空二叉树
{
QueuePush(&q, root);//将root节点push到队列中
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);//拿到队列首元素
QueuePop(&q);//将出队列的节点从队列中删掉
if (front->left)
{
QueuePush(&q, front->left);//将左子节点push到队列中
}
if (front->right)
{
QueuePush(&q, front->right);//将右子节点push到队列中
}
printf("%d ", front->data);//打印出队列的数据
}
//对列的销毁
QueueDestory(&q);
}
2.4 层序遍历的应用----完全二叉树的判断
思路:
- 层序遍历,空节点也进队列。注意:在进队列之前,检查一下front是否为空,否则会出现对空指针进行解引用的错误。
- 出到空节点以后,出队列中所有的数据,如果全是空,就是完全二叉树,如果有非空,就不是。
图示:
代码:
//判断一个二叉树是否是完全二叉树
bool BTreeComplete(BTNode* root)
{
Queue q;//队列的创建
QueueInit(&q);
if (root)//判断是否为空二叉树
QueuePush(&q, root);//将root节点push到队列中
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)//出到空的时候退出,后面再进行判断
{
break;
}
//为什么要放到后面push呢?为了防止对空指针进行解引用
QueuePush(&q, front->left);//此时front一定不为空
QueuePush(&q, front->right);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
//空后面出现非空,就说明不是完全二叉树
if (front)
{
return false;
}
QueuePop(&q);
}
//队列的销毁
QueueDestory(&q);
return true;
}
注意:考虑下面这一种情况:
3. 节点个数以及高度等
3.1 二叉树节点个数
两种方法:
方法一
思路:遍历+计数
代码:
int count = 0;
void BTreeSize(BTNode* root)
{
if (root == NULL)
{
return;
}
count++;
BTreeSize(root->left);
BTreeSize(root->right);
}
当然也可以用局部静态变量来存储节点个数但是不推荐,代码如下:
int BTreeSize(BTNode* root)
{
static int count = 0;//只会在第一次进入时初始化一次
if (root == NULL)
{
return count;
}
count++;
BTreeSize(root->left);
BTreeSize(root->right);
return count;
}
当然,上面这两种方法都不好,都没有充分利用链式二叉树递归定义的结构性质,所以上面的两种方法都不推荐。上面的两种方法中第二种尤其不好,特别是在多次计算元素个数的时候,第二次计算如果count没有进行重新初始化操作将会仍然保留上一次计算的值,如果是用的全局变量的话还能每一次计数完之后重新进行赋值,但是第二种方法完全无法改变上一次计数后的残余值。
全局变量在进行多线程时会出现问题,比如在多线程的情况下就会出现问题:多个线程同时使用count这个全局变量,此时就会出现线程安全问题。
当然,也可以在调用函数中定义一个count变量,然后在调用的时候传入count的地址,那么BTreeSize函数必须这样进行定义:
int BTreeSize(BTNode* root,size_t *count)
{
static int count = 0;//只会在第一次进入时初始化一次
if (root == NULL)
{
return count;
}
(*count)++;
BTreeSize(root->left);
BTreeSize(root->right);
return count;
}
方法二(推荐使用)
(递归法)
思路:子问题
1、空树,最小规模子问题,节点数返回0
2、非空,左子树节点个数 + 右子树节点个数 + 1(自己)
代码:
int BTreeSize(BTNode* root)
{
return root==NULL ? 0 :
BTreeSize(root->left) +
BTreeSize(root->right) + 1;//1在最前面就是前序,在中间就是中序,在最后就是后序
}
这种方法充分利用了链式二叉树递归定义的结构性质,因为链式二叉树的本质就是由根节点和两个子树构成,求整个子树就是求根节点的节点数1加上左右子树的节点个数。
如图所示:
注意:图中紫色数字代表返回值,黑色数字代表相应函数的返回的值。
运用的算法思想:分治
分治:把复杂的问题,分成更小规模的子问题,子问题再分成更小规模的子问题······直到子问题不可再分割,直接能得出结果。
3.2 二叉树叶子节点的个数
方法一
思路:(遍历+计数)
和上面的思路基本一致,只是在count++的前面加上一个叶子节点的判断条件,进而达到对叶子节计数的目的。
代码:
int count = 0;
void BTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL&&root->right==NULL)
{
count++;
}
BTreeLeafSize(root->left);
BTreeLeafSize(root->right);
}
方法二
思路:采用分治的思想。
代码:
int BTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL&&root->right==NULL)
{
return 1;
}
else
return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}
当然,上面的代码可以简写为下面的代码:
int BTreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return root->left == NULL && root->right == NULL ?
1 + BTreeLeafSize(root->left)+ BTreeLeafSize(root->right) :
0 + BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
1 + BTreeLeafSize(root->left)+ BTreeLeafSize(root->right) :
0 + BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
}
[外链图片转存中...(img-iLh1OyA1-1715698777531)]
[外链图片转存中...(img-tLRAkZL7-1715698777531)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**