爆ぜろリアル!弾けろシナプス! パニッシュメント|

OIRikka

园龄:12天粉丝:4关注:9

2025-02-27 14:17阅读: 7评论: 0推荐: 0

[数据结构]树

树(基础)

1 定义

1.1 树是什么

树是一种数据结构,因为形似倒着的树而得名. 树是一种特殊图

1.2 树的定义

递归定义

1.2.1 有根树的定义

形象化的,如图1,有根树存在根节点这一定义,从根节点可以分出任意个分支,这任意个分支又可以继续细分,分出的节点称为“子节点”。


抽象化的,树也是N个节点和N1条边的集合。


每条边都将某个节点连接至他的父亲,而除去根节点外的每个节点都有父亲,每个结点之间互不相交。

1.2.2 无根树的定义

形象化的,无根树就是有根树删去根节点后得到的东西


抽象化的,无根树也是树是N个节点和N1条边的集合。


每条边都将某个节点连接至他的父亲,每个节点互不相交

图1 有根树

1.3 有关树的一些常用术语

森林:每个连通块都是树的图。按照定义,一棵树也是森林


叶节点:没有子节点的节点


父亲:一个节点上一层的点


祖先:一个点上层的每一个点


子节点:一个节点延伸出的下一个节点


深度:一个点到根节点的层数(边数)


高度:从叶节点到根节点的层数。


兄弟:同一个父节点的子节点互为兄弟


后代:一个点下层的每一个点是当前点的后代。


:一个节点的子树个数 一个节点生的子节点的个数(其实这个概念应该放在前面,大家可以试着使用度来概括一些前期概念)


内部节点:根以外的分支节点


树的度:这棵树各节点中度的最大值


图2 数的术语

1.4 特殊树

:满足与任意节点相连的边不超过2条的树


二叉树:每个节点最多只有两个子节点的树。


完整二叉树:每个节点的子节点数量都为2没有


完全二叉树:只有最下面两层节点度数可以<2,且最下面一层节点都位于该层最左边的位置上。


完美二叉树:所有叶节点深度相同,且所有节点都只有两个子节点!


注意!完美二叉树一定也是完全二叉树和完满二叉树,但完满二叉树不一定是完全二叉树和完美二叉树。





1.5 二叉树的一些性质

性质部分:

1.在二叉树的第i层上最多有2i1个节点

2.深度为k的二叉树最多有2k1个节点

3.对任意一棵二叉树,如果其叶节点数为n0,度为2的节点数为n2,则一定满足n0=n2+1

4.具有n个节点的完全二叉树的深度为floor(log2n)+1

5.对于一棵n个节点的完全二叉树,对任意一个节点i,有:


5.1 如果i=1,则节点i为根,无父节点;

5.2 如果i>1,则其父节点编号为i/2

5.3 如果2i>n,则节点i是叶节点,否则左孩子编号为2i

5.4 如果2i+1>n,则节点i无右孩子,否则右孩子编号为2i+1



证明部分:

用归纳法证明性质1.

i=1时,2i1显然成立,现在假设第i1层时命题成立,即第i1层上最多有2i2个节点。由于二叉树的每个节点的度做多为2,故在第i层上的最大节点数为第i1层的2倍,即22i2=2i1

2 树的储存

2.1 普通树的储存

2.1.1 顺序存储

1.父亲表示法(数组记录孩子的父亲为父亲)

int data[N];//存数据的
int father[N];//father[i]=j 表示i的父亲为j

2.孩子表示法(数组记录父亲的第任意个孩子为孩子)

int data[N];//不做过多解释
int son[N][M];//son[i][j]=k表示父亲i的第j个孩子为k

3.父亲孩子表示法(双向奔赴的爱)

int data[N];//不做过多解释
int father[N];//father[i]=j 表示i的父亲为j
int son[N][M];//son[i][j]=k表示父亲i的第j个孩子为k

4.孩子兄弟表示法(见故事)

int data[N];//不做过多解释
int firstson[N];//表示父亲的第一个孩子
int nxt[N];//nxt[i]=j表示i的下一个兄弟为j

这里我们老师给我们讲了个故事,说乾隆生了100多个孩子,这时候怎么记是不是自己孩子呢?就让哥哥记住自己弟弟,弟弟在记住自己弟弟,这就是孩子兄弟表示法。

2.1.2 链式存储

1.父亲表示法

struct node
{
    int data;// 节点存储的数据
    node *father// 指向父节点的指针
}tree[N];//树

2.孩子表示法

struct node
{
    int data;// 节点存储的数据
    node *son[M];//指向子节点的指针
}tree[N];

3.父亲孩子表示法

struct node
{
    int data;//数据
    node *father;//指向父亲
    node *son[M];//指向孩子
}tree[N];

4.孩子兄弟表示法

struct node
{
    int data;//数据
    node *firstson;//第一个儿子
    node *bro;//下一个兄弟
}tree[N];

2.2 二叉树的存储

1.顺序存储

int data[N];//数据域

2.链式存储

struct node
{
    int data;
    node *lc;//左孩子
    node *rc;//右孩子
    node(int d)//构造函数
    {
        data=d;// 初始化节点数据为传入的参数d
        lc=NULL;// 初始化左子节点指针为NULL
        rc=NULL;// 初始化右子节点指针为NULL
    }
    //另一种写法
    /*
      node(int d) : data(d), lc(NULL), rc(NULL)
    {

    }
    */
};
node *bt;//根节点指针

3.树的遍历

先序遍历,中序遍历,后序遍历

3.1 先序遍历

如图8,先序遍历类似DFS,从根开始,然后是左子树,递归到最深层后返回,然后遍历右子树,最后回到根



void preoder(int f)
{
    cout<<data[f]<<" ";//首先在函数运行前输出是为了先从根节点开始
    if(lc[2*f]>0)//左子树
        preoder(2*f);
    if(rc[2*f+1]>0)//右子树
        preoder(2*f+1);
}

3.2 中序遍历

如图9,中序遍历在遍历完左子树后直接跳向根节点后遍历右子树



void inoder(int f)
{
    if(lc[f]>0)
        inoder(lc[f]);
    cout<<data[f]<<endl;//在遍历左子树时输出是为了先从左子树开始
    if(rc[f]>0)
        inoder(rc[f]);
}

3.3 后序遍历

如图10,后序遍历在遍历完左子树后直接跳向右子树叶子节点后向上遍历至根节点



void postoder(int f)
{
    if(lc[f]>0)
        postoder(lc[f])
    if(rc[f]>0)
        postoder(rc[f]);
    cout<<data[f]<<endl;//在遍历右子树时输出是为了从右子树开始
}

3.4 FAQ

肯定会有彭于晏有疑问,为什么他们的输出位置不同,决定了他们的遍历次数不同?

1.先序遍历 首先在函数运行前输出是为了先从根节点开始

2.中序遍历 在遍历左子树时输出是为了先从左子树开始

3.后序遍历 在遍历右子树时输出是为了从右子树开始

神不神奇?反正我觉得挺神奇

接下来需要完善的:

2.二叉树性质 证明

4.反推

5.BFS

6.Morris

呼~~ 用时7个多小时终于写完了!希望能帮到你喵~~

本文作者:OIRikka

本文链接:https://www.cnblogs.com/rikkkkka/p/18740874

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   OIRikka  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起