当前位置: 首页 > news >正文

二叉树(建立 + 遍历 + 拓展)

今天学习了二叉树的入门,所以写一篇博客浅谈对这个树状结构的理解。

二叉树,顾名思义一个父节点有两个儿子节点,主要分为满二叉树、完全二叉树、二叉搜索树等等(主要是我看视频上说的,我还没学到),今天先学了3道洛谷上普及-的二叉树的遍历的题和一道普及/提高-的综合一点的题目(用到了求最近共同祖先)。

二叉树的构建:

首先是二叉树的构建,因为是二叉树,所以可以用一个结构体数组,结构体中包含left(左儿子)和right(右儿子),输入的时候直接赋值即可,但是推荐用二维vector来建数,这样可以更满足多种情况,详细在之后的题目中有提到。

二叉树的遍历:

目前我只学习了二叉树的dfs(深度优先搜索)的遍历方式,dfs分为三种:

1.前序遍历:中 - 左 - 右

2.中序遍历:左 - 中 - 右

3.后序遍历:左 - 右 - 中(找最近共同祖先的时候一定要用后序遍历!!!)

在二叉树的遍历的时,其实这几种遍历方式都可以,一般可能前序遍历更熟悉一点。

基础入门练习题

二叉树深度 

这道题就是正式接触二叉树的入门题了,求深度就需要建立好这个树,然后用dfs去搜索即可。

建树的时候就要记录每一个父节点的两个子节点,然后搜索在递归的时候直接传入的参数就是子节点和深度即可。听起来 抽象,看代码就能理解了!

// Problem: P4913 【深基16.例3】二叉树深度
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4913
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 1e6+10;
struct tree
{int l,r;
}a[N];
int ans=0;
void dfs(int val,int deep)//val是当前的节点(不如说是当前的子树的根节点!!!),deep是当前的深度
{if(val==0) return;//如果当前的节点没有子节点了说明当前就是叶子节点ans = max(ans,deep);//记录深度最大值dfs(a[val].l,deep+1);//前序遍历不断递归当前根节点的左子树dfs(a[val].r,deep+1);//不断递归当前根节点的右子树
}
void solve()
{int n;cin>>n;for(int i=1;i<=n;i++){cin>>a[i].l>>a[i].r;//将每一个父节点的两个子节点存起来}dfs(1,1);//初始化(整棵树的根节点为1,当前深度为1)cout<<ans<<endl;
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

看完这篇代码解析,是不是对二叉树是遍历有了一定的概念呢,其实就是用了dfs搜索和递归的思想,那么下面就用三种不同的遍历方式来遍历二叉树吧!

二叉树的遍历

这道题就是对三种遍历方式的综合运用,代码已经非常非常详细了,不多赘述了。 

// Problem: B3642 二叉树的遍历
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/B3642
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 1e6+10;
struct tree
{int l,r;
}a[N];
vector<int> ans1,ans2,ans3;
/*1.确定递归函数的参数和返回值2.确定终止条件3.确定单层递归的逻辑
*/
void front_dfs(int mid)//前序遍历 mid为当前父节点
{if(mid==0) return ;//递归终止条件 遍历到叶子节点就returnans1.push_back(mid);//中子树front_dfs(a[mid].l);//左子树front_dfs(a[mid].r);//右子树
}
void mid_dfs(int mid)//中序遍历 mid为当前父节点
{if(mid==0) return ;//递归终止条件 遍历到叶子节点就returnmid_dfs(a[mid].l);//左子树ans2.push_back(mid);//中子树mid_dfs(a[mid].r);//右子树
}
void back_dfs(int mid)//后序遍历 mid为当前父节点
{if(mid==0) return ;//递归终止条件 遍历到叶子节点就returnback_dfs(a[mid].l);//左子树back_dfs(a[mid].r);//右子树ans3.push_back(mid);//中子树
}
void solve()
{int n;cin>>n;for(int i=1;i<=n;i++) cin>>a[i].l>>a[i].r;//建树front_dfs(1);//以第一个父节点1开始前序遍历mid_dfs(1);//以第一个父节点1开始中序遍历back_dfs(1);//以第一个父节点1开始后序遍历for(auto i : ans1) cout<<i<<' ';cout<<endl;for(auto i : ans2) cout<<i<<' ';cout<<endl;for(auto i : ans3) cout<<i<<' ';
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

新二叉树

刚开始看到这道题没有思路,不知道怎么用数组取存字符二叉树,后来想到了字符的ASCII码,所以我们就可以用数字来代替这几个字母!思路一下子就清晰了。这道题用的也是前序遍历。解题思路就是在输入的时候先对输入的数据进行转为数字处理再存入树中即可。

// Problem: P1305 新二叉树
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1305
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 30;
struct tree
{int l,r;
}a[N];
void front_dfs(int mid)
{if(mid==-1) return ;// cout<<('a'+mid);printf("%c",'a'+mid);//使用字符加数字输出的时候用printf的%c强制转为字符类型输出即可front_dfs(a[mid].l);front_dfs(a[mid].r);
}void solve()
{int n;cin>>n;int start=0;for(int i=1;i<=n;i++){char k,x,y;cin>>k>>x>>y;int kk = k - 'a';//对输入的字符进行转数字处理int xx = x - 'a';int yy = y - 'a';if(x=='*') xx = -1;//说明没有左儿子节点 赋值为-1if(y=='*') yy = -1;//说明没有右儿子节点 赋值为-1a[kk].l = xx;a[kk].r = yy;if(i==1) start = kk;//记录下来搜素的起始位置(整棵树的根节点)}front_dfs(start);
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

接下来就是比较有意思的二叉树的综合问题了:

二叉树问题

这道题我更推荐用二维vector来建树因为输入的时候没有将左右子节点一块读入。

深度好求,直接就是用dfs神兽一遍即可,跟第一题一样的。

宽度呢?我看好多人都用了bfs一层一层的统计,其实细心点就会发现,在我们dfs求最大深度的过程中,我们就已经遍历到了每一个点,并且也有这个点的深度参数,所以我们就可以用一个数组来存放每一个深度中有几个节点(即在遍历每一个点的过程中将其所在的深度++即可,由于没有用到回溯的思想,所以每一个点只会统计一次!)最后在dfs完找到最深值的同时也就把这个桶数组更新完了,只需要遍历一遍桶数组找出最大值即可!

最后就是两点之间的距离了,题解上是用的最近共同祖先,但是我找到一个更好的思路,当然最近共同祖先我也会在下文中提到的,因为题目中给定的x到y距离的定义为x向上的边数的二倍加y向上的边,我们可以从x点不断地往上去搜他的父节点(事先用一个数组在输入数据的时候就存起来)直到根节点1,这样就找到了所有x的祖先,同时用一个数组来存放x的每一个祖先到他的最小距离,然后用同样的方法去往上遍历y,注意这时候遍历就不能一直遍历到根节点1了,只有在没有遍历到x的祖先的时候(x没有经过的点)时,才能继续遍历,如果遍历到了x经过的点(说明当前的点就是x的一个祖先,那么同时也是y的一个祖先,那此时这个祖先就是x和y两个点的最近的共同祖先了)!太妙了!代码实现不难:

// Problem: P3884 [JLOI2009] 二叉树问题
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3884
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define int long long 
#define endl '\n'
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define PII pair<int,int>
#define fi first
#define se second
const int N = 110;
vector<int> a[N];
int deep;
int kuandu[N];
int fa[N]; //fa[i]表示i结点的父亲
int dist[N]; //dist[i]表示i到x的最短距离void shendu(int x,int step)//求深度
{if(x==0) return ;kuandu[step]++;//更新该宽度下的节点数deep = max(deep,step);for(auto i : a[x]){shendu(i,step+1);}
}
void solve()
{int n;cin>>n;for(int i=1;i<n;i++){int x,y;cin>>x>>y;a[x].push_back(y);fa[y]=x;}shendu(1,1);cout<<deep<<endl;int max_kuandu=0;for(int i=1;i<=deep;i++) max_kuandu = max(max_kuandu,kuandu[i]);//同时找到最大宽度cout<<max_kuandu<<endl;int x,y;cin>>x>>y;dist[x]=0;//x到x的距离是0while(x!=1)//向上爬到不能再爬{dist[fa[x]]=dist[x]+1;//记录上层节点到x的距离(每一个祖先到x的距离)x=fa[x];//x往上爬一层}int len=0;while(y!=1&&dist[y]==0)//没经过x经过的点,dist的值为0 经过了就说明找到最近共同祖先了!{len++;//每向上一层路径加1y=fa[y];//y向上爬一层}cout<<dist[y]*2+len; 
}signed main()
{IOSint T=1;
//	cin>>T;while(T--) solve(); return 0;
} 

最近共同祖先

最近共同祖先的视频讲解:二叉树的最近共同祖先

其实上面的的用父节点数组存储的方式就已经是单次O(N)的时间复杂度了,如果想优化的话就需要用到一些如倍增法、Tarjan离线算法等一下高级算法了(我还没学,等我以后了解并有了自己的理解之后就对这一块的内容进行补充的)

http://www.dtcms.com/a/286656.html

相关文章:

  • 外部DLL创建及使用
  • 灵巧手(具身智能入门十一)
  • if (a == 1 a == 2 a == 3)返回true的问题思考
  • NVIDIA 驱动安装失败问题排查与解决(含离线 GCC 工具链安装全过程)
  • MySQL组内拼接group_concat函数
  • MyUI会员排名VcMember组件文档
  • Java与Vue技术搭建的SRM招标采购管理系统,提供源码,涵盖招标、投标、评标全流程,助力企业高效规范采购管理
  • spring-cloud微服务部署-feign服务间调用
  • NFS读写性能评估与优化指南(下)
  • 二叉搜索树:高效的查找结构
  • 自学力扣:最长连续序列
  • python-pptx 的layout 布局
  • CCF编程能力等级认证GESP—C++1级—20250628
  • 扫地机器人,需要回归第一性原理
  • Docker安装教程
  • Visual Studio C++编译器优化等级详解:配置、原理与编码实践
  • 第七章 愿景07 实习小宇
  • LLC电源设计专题--详细讲解
  • Web开发 02
  • 贪吃蛇(C++实现)
  • 美客多跨境电商平台怎么开店?美客多入驻门槛有哪些?
  • 目标框的位置以及大小的分布
  • 进入当前正在运行的 Docker 容器
  • 应急响应-Windows资源监视器
  • 易用性强短视频矩阵平台源头开发商推荐
  • leetcode:单词接龙[图广搜][无权图找最短路径]
  • 突破性量子芯片问世:电子与光子首次集成,开启量子技术规模化应用新篇章
  • 跨平台猫咪键盘桌宠BongoCat v0.6.2 绿色版(附带多款皮肤包)
  • 集训Demo4
  • 【DEBUG】Debug日志001:RL项目记录