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

树基础

#define 柚子树 右子树

0x00 树基本

0x01 树

树是啥?一种抽象但相当有用的数据结构。
树的结构特殊性使得它易于计算信息,并用来出各种各样的题。其各种变体还可以用于维护信息。
我们听说过的线段树、树状数组、树上启发式合并等高级方法均离不开树。可以说,没有树就不可能有OI。

下面便是一个树(oi-wiki):

定义:一个无环、连通的简单无向图。图是啥

对于树上的点u,可以定义它的父节点、兄弟节点、子节点,以及祖先与子孙。
没有父节点的点成为根。没有子节点的点成为叶子。
树可根据有无确定的根进行分类。 无根树可任意指定一个根。
树上的点u的子树:u及其所有子孙节点。

点的深度:到根节点的边数。 树的高度:所有点深度的最大值。
树的宽度:这个真的有啥用吗

0x02 二叉树

每个点子节点个数均不超过2的树称为二叉树。

满二叉树:每一层节点数都达到最大值的树。

完全二叉树:除了最后一层都是满的,且最后一层所有点都集中在左边的二叉树。满二叉树为特殊的完全二叉树。

二叉树的一些性质:

  1. 对于第k层,二叉树最大节点数为 \(2^{k-1}\)
  2. 有k层的满二叉树节点数为 \(2^k-1\)
  3. 一个n个节点的二叉树,其深度最大为n(链),最小为 \(\log n+1\) (完全二叉树)。
  4. 二叉树中,有2个孩子的点个数 = 叶子个数-1.
  5. 有n个叶子的完全二叉树有 \(2n-1\) 个点。

0x10 树的存储与遍历

0x11 存储

父亲存储法

直接存储父亲节点的方法,适用于任何树,但不好用,一般只用于并查集。

数组直接存法

只用于完全二叉树。
对于任意一个完全二叉树,我们按bfs的顺序直接把所有点放进一个数组。
可以发现,对于任意一个下标为u的点,其左子节点下标为u<<1,柚子节点下标为u<<1|1。

如果你不能简单的判断一个点是不是叶子,最好把空间开2倍。
普通线段树也是用这种方法,但要开4倍空间。

左右儿子法

适用于二叉树。
方法:使用数组模拟链表。
每个点开一个结构体,存储它的左右儿子下标和其他信息。
以结构体类型开一个足够大的内存池。对于新点,从内存池中拿一个空的内存分配给它,然后更新它的、它父亲的左右子树即可。
没有删除节点操作时,不需要开额外空间。

应用:动态开点线段树、各种平衡树等。

struct Node{int val;//点权等信息int lc,rc;// 存左子结点的位置、存柚子节点的位置
}tr[N];// 内存池
int tot,rt;// 当前节点个数、树根
inline int New(int v){// 返回新加点的下标++tot;tr[tot].val=v;// anything elsereturn tot;
}

转二叉树法

又称孩子兄弟表示法。适用于多叉树。将多叉树转成二叉树后用左右儿子法存储。
对于一棵多叉树,断掉原来所有的边,将每个点与它第一个孩子连一条边,最靠近它的第二个孩子连一条边,这样即可建出一棵与原树基本等价的二叉树。
用处不大,可能在初赛考。

儿子数组法

每个点开一个数组存其儿子。
适用于任何树,大多数情况下被邻接表替代,一般只用于trie。

邻接表法

万能的方法。适用于一切树、森林、边带方向的树等。适合给你一个树让你维护树上信息,或计算各种各样的答案的题。

一般树的存储还有前文所述父亲法、儿子法、转二叉树法,都有点用但不多。

邻接表:来自图论,本质为一排链表。
将每个点的邻接点用链表串起来,就成了一个存着树/图的邻接表。

实现方法:2种。

最简单的方法:vector
优点:好写、不用考虑空间的问题(用手写链表存无向图不开两倍就会挂大分)
代码:

struct EDGE{int v,w;};
vector<EDGE> g[N];g[u].emplace_back(EDGE{v,w});
// g[v].emplace_back(EDGE{u,w});  //无向图// 遍历u的邻接点
for(EDGE to:g[u]){int v=to.v, w=to.w;// 邻接点、边权// anything else
}

另一种方法:手写链表法。
一般写头插法。
代码:

struct Node{int to,w,Next;}g[M];// 无向图要开2倍
int h[N];// 头指针// 加边
void Add(int u, int v, int w){num++; g[num].to=v; g[num].w=w;g[num].Next=h[u];h[u]=num;
}// 遍历u的邻接点
for(int i=h[u]; i; i=g[i].Next){int v=g[i].to, w=g[i].w;//anything else
}

0x12 遍历

DFS遍历
相当于以根节点为起点在树上进行深搜。
代码:

void Dfs(int fa, int u){for(EDGE to:g[u]){int v=to.v, w=to.w;if(v==fa) continue;// 避免走回父亲节点Dfs(u,v);}
}

对于二叉树,有三种特殊的遍历方法:
先序遍历:先访问根,再递归遍历左右子树:

void Rlr(int u){if(!u) return;cout<<u<<" ";Rlr(tr[u].lc); Rlr(tr[u].rc);
}

中序遍历:先递归遍历左子树,再访问根,然后递归遍历右子树:

void lRr(int u){if(!u) return;lRr(tr[u].lc);cout<<u<<" ";lRr(tr[u].rc);
}

后序遍历:先访问根,再递归遍历左右子树:

void lrR(int u){if(!u) return;lrR(tr[u].lc); lrR(tr[u].rc);cout<<u<<" ";
}

BFS遍历
相当于以根节点为起点在树上进行广搜。
代码:

inline void Bfs(int s){q.push(s);int u;while(!q.empty()){u=q.front(); q.pop();vis[u]=1;// 需要打标记for(int v:g[u]){if(!vis[v])q.push(v);}}
}
http://www.zskr.cn/news/75018.html

相关文章:

  • 12.6笔记
  • 完整教程:新手做网站如何被百度快速收录教程
  • smartbits是啥
  • vxe-gantt 甘特图实现产品进度列表,自定义任务条样式和提示信息
  • Linux内核学习记录
  • ret2libc+一点点保护
  • 详细介绍:【数据库】国产数据库替代实战:金仓KES如何以“智能运维 + 低资源占用”年省百万运维成本?
  • 2025年广东地区湘菜供应链江西小炒社区厨房称重自选食材配送服务商TOP5评测!全品类供应+定制化服务权威榜单发布,赋能餐饮高效运营
  • 『NAS』在群晖部署一款好看的白板工具-Excalidraw
  • Ubuntu下,MySQL密码遗失时修改密码
  • 2025最新贵州伴手礼厂家/采购渠道/供应商/平台/卖场/超市TOP5推荐!地道风物+文化赋能权威榜单发布,甄选贵礼传递黔地心意
  • 001.makdown快速入门
  • Oracel VirtualBox安装Windows11时无法找到ISO文件或不满足系统要求
  • 构建个人知识库新选择:深度解析访答本地私有知识库
  • AIShareTxt入门:快速准确高效的为金融决策智能体提供股票实用的技术指标上下文
  • I know only one topic but I wear glasses in 20s
  • 云原生基石的试金石:基于 openEuler 部署 Docker 与 Nginx 的全景实录 - 指南
  • qemu如何和宿主机共享文件 - show
  • 2025贵州贵阳荣和酒坊采购渠道推荐!百年传承酱香白酒购买平台TOP5榜单发布,品味历史沉淀的醇香佳酿
  • UE5循环播放蒙太奇
  • 完整教程:神经网络—— 学习与感知器
  • 智能座舱
  • 团体设计天梯赛L1题解
  • 完整教程:乡镇外卖跑腿小程序开发实战:基于PHP的乡镇同城O2O
  • 关于博客后续
  • 2025.12.6博客
  • 英语_阅读_a robot for science fair_待读
  • 2025年什么牌子的轮胎比较好:权威测评优质轮胎排行
  • faster r cnn中的动量
  • 需求的分层