#昨天我们把树的基础知识部分给讲完啦,那我们今天趁热打铁,直接我们来手搓树的代码部分,这里我会让每个人都懂得哟!!
这里我们紧接着昨天的二叉查找树来继续说明。
二叉查找树的创建
我们昨天学习可以知道,二叉查找树的定义是:左子树小于根节点小于右子树。也是有一定顺序的排列的二叉树。
结点的建立:
我们要知道,一个树的最小元素为结点:包括了左孩子,右孩子,根节点,那我们应该怎么表示他们呢?
用结构体,这样就可以实现三者的联动,代码部分如下:
struct treenode{
int data;
treenode* lchild;
treenode* rchild;
treenode(int v){
data=v,lchild=NULL,rchild=NULL;
}
};
PS:在这个结构体中,下面的treenode(int v)这一部分用于树的初始化,可以不在结构体中添加,在下面的循环中添加也是可以的。我就直接在这里添加了,方便说明,也便于记忆。
树的建立:
写完结点的结构之后,我们就要思考,每一个结点的开辟都要空间,所以我们应该每一次新建一个结点就要new开辟一个新的空间。
我们首先应该把这棵树的第一个元素当作根节点存储起来,同时开辟一个空间。如下:
void build(vector<int> a)
{
treenode* root=new treenode(a[0]);
这样我们的第一个根节点就算是储存下去了,之后我们要做的就是构建二叉查找树。
我们再次回忆一下定义:左子树小于根节点,根节点小于右子树。
这样我们的逻辑不就出来了嘛?
我们在for循环中,逐个比较各个元素和根节点的大小,如果比根节点大,那我们就排在右边,同时,如果右边已经存在了右子树,我们应该继续和右子树比较,如果比他大,同时没有了子节点,我们就可以把这个元素存进去右子树的右孩子这个位置。左子树同理。
逻辑就是:1.先和根节点比较大小 2.判断左子树(右子树)是否为空 3.若为空就储存进去 4.若不为空则重复前三个步骤。
那我们的代码部分也就出来啦,代码如下:
for(int i=1;i<a.size();i++)
{
treenode* tmp=new treenode(a[i]);
treenode* troot=root;
while(troot){
if(tmp->data<troot->data){
if(troot->lchild==NULL)
{
troot->lchild=tmp;
break;
}
else troot=troot->lchild;
}
else if(tmp->data>troot->data){
if(troot->rchild==NULL)
{
troot->rchild=tmp;
break;
}
else troot=troot->rchild;
}
}
}
之后我们结合上面两个步骤,我们就可以得到了我们搜索二叉树的创立过程啦:
代码如下,要多思考,同时这个模板也要多打,多记:
#include<bits/stdc++.h>
#include<vector>
using namespace std;
struct treenode{
int data;
treenode* lchild;
treenode* rchild;
treenode(int v){
data=v,lchild=NULL,rchild=NULL;
}
};
void build(vector<int> a)
{
treenode* root=new treenode(a[0]);
for(int i=1;i<a.size();i++)
{
treenode* tmp=new treenode(a[i]);
treenode* troot=root;
while(troot){
if(tmp->data<troot->data){
if(troot->lchild==NULL)
{
troot->lchild=tmp;
break;
}
else troot=troot->lchild;
}
else if(tmp->data>troot->data){
if(troot->rchild==NULL)
{
troot->rchild=tmp;
break;
}
else troot=troot->rchild;
}
}
}
return root;
}
int main()
{
vector<int> a{1,2,3,4,5,6,7,8,9};
treenode* root=build(a);
return 0;
}
前中后序遍历:
那我们现在已经得到了我们的搜索二叉树。那我们如何实现我们的前中后序遍历呢?不遍历的话我们就无法将这个树给表示出来,那我们应该怎么遍历呢???
其实很好想,我们大概猜一下也就知道,我们应该用递归的思想,是吧?这样的话我们的代码会比较简洁,同时也很高效,就是不是很好想0.0🤔,其实也还好。
我们首先要判断根节点是否为空?如果为空,那我们直接结束递归,根节点为空,说明树不存在,我们也没有讨论的必要了。之后如果是中序遍历,那我们就首先返回左子树,输出根节点,在递归右子树不就实现中序遍历了吗?
代码如下:
void zhong(treenode* root)
{
if(root==NULL)return ;
zhong(root->lchild);
cout<<root->data;
zhong(root->rchild);
}
层次遍历:
我们用前中后序遍历并不能很好地反映出我们树的结构,而我们如果可以一层一层的输出树的结构,这样不就很清晰明了了嘛?这就要请到我们的层次遍历大将军出场啦。
我们首先要想层次遍历的逻辑,怎么实现层次遍历?
层次遍历:一层一层的输出,像极了一个队列,我们首先进去第一层,然后第一层再出来,第二层再进去,第二层再出去。是不是先进先出??那我们就可以用队列来模拟这个过程。
1.队列应该先把根节点导入进去,我们判断队列结束的条件就是队列是否为空
2.创立临时变量等于队列的最前面的元素,之后弹出这个元素,输出这个元素所对应的数值
3.判断这个元素的左右子树是否为空,如果不为空我们就push进去,这样就相当于把下一层给导入了进去。
那我们的代码部分就出来啦:
void layersearch(treenode* root)
{
queue<treenode*>q;
q.push(root);
while(!q.empty())
{
treenode* tmp=q.front();
cout<<tmp->data;
q.pop();
if(tmp->lchild!=NULL)
{
q.push(tmp->lchild);
nlast=tmp->lchild;
}
if(tmp->rchild!=NULL)
{
q.push(tmp->rchild);
nlast=tmp->rchild;
}
}
}
}
这样输出的话是一列数字,如果我们想要实现换行操作呢???
我们应该在代码里面怎么判断换行操作呢??
我们因该判断当我们的临时变量等于了我们的根节点,我们就换行。
这样说的是不是很不好理解?我们拿我们最早的根节点来说明,第一行就一个元素,是不是要求我们的临时变量等于我们的根节点,我们就换行?
后面也好理解,因为我们的根节点一直在变化,不断的赋值给临时变量,这就很好理解啦,同时我们也可用这种办法求出我们的树高。
代码如下啦:
void layersearch(treenode* root)
{
queue<treenode*>q;
q.push(root);
treenode* last=root;
treenode* nlask=NULL;
while(!q.empty())
{
treenode* tmp=q.front();
cout<<tmp->data;
q.pop();
if(tmp->lchild!=NULL)
{
q.push(tmp->lchild);
nlast=tmp->lchild;
}
if(tmp->rchild!=NULL)
{
q.push(tmp->rchild);
nlast=tmp->rchild;
}
if(tmp==last){
cout<<endl;
h++;
last=nlast;
}
}
}
//以上就是我们搜索二叉树的相关知识啦,当然如果我们求树高,并不用这种麻烦的方法,在最后一张的知识体系中,我会教大家用递归的办法来求树高,这样的话会十分节约我们的时间,不用打那么多的代码,同时也简洁美观。还有一些零碎的知识点,我都会在后面的章节里面给大家写到哦。
如果文章对大家有帮助,希望大家多给点关注啦,码字不易,求点赞关注啦~~~~~~