数据结构——树

目录

正文

树的基本概念

1. 树的定义
树是一种数据结构,它是由n(n≥0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

每个节点有零个或多个子节点;
没有父节点的节点称为根节点;
每一个非根节点有且只有一个父节点;
除了根节点外,每个子节点可以分为多个不相交的子树。
树的图像

树的基本术语
  • 结点的度和树的度:某个结点的子树个数为结点的度, 树中所有的结点的度中的最大值叫做树的度
  • 分支结点与叶子结点:分支结点就是度不为零的节点可以分为单分支结点(度为1)和 n 分支结点(度为n),叶子结点就是度为0的结点如上图标‘#’的结点都是叶子结点
  • 路径和路径长度: 树种存在一个结点序列( k 1 k_1 k1, k 2 k_2 k2, k 3 k_3 k3 k n k_n kn),可以从 k 1 k_1 k1 走到 k n k_n kn就称该结点序列为一条路径路径长度就是该路径所通过的结点数目减去1即可。
  • 孩子结点、双亲结点、兄弟结点: 每个结点的后继节点被称为孩子结点(如B、C就是A的孩子结点), 兄弟结点就是有同一个双亲的结点(B和C有同一个双亲A所以B和C就是一对兄弟结点)所以双亲结点就显而易见了。
  • 结点层次和树的高度: 树中结点层次的最大值为树的高度结点层次根的层次为0,根的直接左右孩子层次为1,以此类推层次逐渐递增。
树的性质
  • 性质1: 树的结点数等于所有结点数的度数之和加1(这个1就是根节点的度)
  • 性质2: 度为m的树的第i层上最多有 m i − 1 m^{i - 1} mi1个结点
  • 性质3: 高度为h的m次树最多有 m h − 1 m − 1 \frac{m^h - 1}{m - 1} m1mh1个结点
  • 性质4: 具有n个结点的m次树的最小高度为[ log ⁡ m ( n ( m − 1 ) + 1 ) \log_m{(n(m - 1)+1)} logm(n(m1)+1)]

其中有一些比较重要的公式

在m次树中计算结点个数时常用的关系有
1.所有的结点的度之和 = n - 1
2.所有结点的度之和 = n 1 n_1 n1 + 2* n 2 n_2 n2 + 3* n 3 n_3 n3 + … + m* n m n_m nm
3.n = n 1 n_1 n1 + n 2 n_2 n2 + n 3 n_3 n3 + … + n m n_m nm
其中 n 1 n_1 n1代表度为1的结点, 前面的系数为 该类型的结点的个数

以上就是树的一些基本概念和关键字了

二叉树

二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点

Attention:

度为2的中至少存在一个结点的度为2,而二叉树没有这个要求
度为2的不区分左、右子树,而二叉树是严格区分左、右子树的

  • 二叉树的特点
  1. 每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。
  2. 左子树和右子树是有顺序的,次序不能任意颠倒。
  3. 即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。
  • 二叉树的性质
    1)在非空二叉树的第i层上最多有 2 i − 1 2^{i - 1} 2i1个节点(i ≥ \geq 1)
    2)高度为h的二叉树最多有 2 h − 1 2^{h - 1} 2h1个节点。(h ≥ \geq 1)
    3) n 0 n_0 n0= n 2 n_2 n2+ 1 1 1 其中: n 0 n_0 n0表示度数为0的节点数, n 2 n_2 n2表示度数为2的节点数。
    4)在完全二叉树中,具有n个节点的完全二叉树的深度为[ log ⁡ 2 n \log_2n log2n]+1,其中[ log ⁡ 2 n \log_2n log2n]是向下取整。
    5)若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点有如下特性:

(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。

  1. 斜树:所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树。这完全可以脑补出来(就不搞图片了)
  2. 满二叉树:在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
    在这里插入图片描述
  • 非空满二叉树的特点:

(1)叶子结点都在最下一层
(2)只有度为0的结点和度为2的结点

  1. 完全二叉树:对一颗具有n个结点的二叉树按层编号,如果编号为i(1 ≤ \leq i ≤ \leq n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
    在这里插入图片描述
  • 完全二叉树的特点:

(1)叶子结点只能出现在最下层和次下层。
(2)最下层的叶子结点集中在树的左部。
(3)倒数第二层若存在叶子结点,一定在右部连续位置。
(4)如果结点度为1,则该结点只有左孩子,即没有右子树。
(5)同样结点数目的二叉树,完全二叉树深度最小。
(6)当结点总数n为奇数时, n 1 n_1 n1=0,当结点总数为偶数时 n 1 n_1 n1=1。

  1. 先序遍历:
    主要方式为 : 根结点 -> 左子树 -> 右子树
void Preorder(Bnode *root)
{
	if(root)
	{
		print("%c",root -> val);//根
		Preorder(root -> left);//左
		Preorder(root -> right);//右
	}
}
  1. 中序遍历:
    主要方式为: 左子树 -> 根结点 -> 右子树
void Inorder(Bnode *root)
{
	if(root)
	{
		Inorder(root -> left);//左
		print("%c",root -> val);//根
		Inorder(root -> right);//右
	}
}
  1. 后序遍历:
    主要方式为: 左子树 -> 左子树 -> 根结点
void Postorder(Bnode *root)
{
	if(root)
	{
		Postorder(root -> left);//左
		Postorder(root -> right);//右
		print("%c",root -> val);//根
	}
}
为了帮助理解特地献上一份 文章 <----点击跳转
并查集

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。
并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。常常在使用中以森林来表示----来自百度百科

你会惊奇的发现,百度百科中的例题可以使用下面的并查集巧妙的解决

直接上代码hhh

(1)朴素并查集:

    int p[N]; //存储每个点的祖宗节点

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);


(2)维护size的并查集:

    int p[N], size[N];
    //p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        size[i] = 1;
    }

    // 合并a和b所在的两个集合:
    size[find(b)] += size[find(a)];
    p[find(a)] = find(b);


(3)维护到祖宗节点距离的并查集:

    int p[N], d[N];
    //p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离

    // 返回x的祖宗节点
    int find(int x)
    {
        if (p[x] != x)
        {
            int u = find(p[x]);
            d[x] += d[p[x]];
            p[x] = u;
        }
        return p[x];
    }

    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        d[i] = 0;
    }

    // 合并a和b所在的两个集合:
    p[find(a)] = find(b);
    d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量

鸣谢
本文部分内容来自此

作者:MrHorse1992
链接:https://www.jianshu.com/p/bf73c8d50dc2
来源:简书

并查集代码来自:
作者:yxc
链接:https://www.acwing.com/blog/content/404/
来源:AcWing

感谢你的阅读 :)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

He_xj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值