数据结构:树


1.数据结构导论

1.1.基础定义

树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因
为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
在这种层次结构中有一个结点具有特殊的地位,这个结点称为该树的根结点,或称为树根。

这是一颗树

术语 描述
根结点 每棵树都有一个根结点 50为这棵树的根节点
子节点 一个节点的后继节点被称为子节点 50的子节点为27,75
父节点 若一个节点含有子节点,则这个节点称为其子节点的父节点 50是27,75的父节点
兄弟节点 具有相同父节点的不同节点 61和85是兄弟节点
节点的度 一个节点含有的子节点的个数称为该节点的度 75的度为2,61的度为1
叶节点 度为0的节点称为叶节点 67、81都是叶子节点
树的深度 树中节点的最大层次 最深的节点为67,深度为4
节点的祖先 从根到该节点所经分支上的所有节点 50,75,61,71都是67的祖先
子孙 所有子节点以及子节点的子节点以及… 所有节点都是50的子孙
子树 以某节点为根的树 以75为根的子树包括7个节点
父子节点直接存在一条连边 50和27之间有条边

1.2.树的特性

一棵树中任意两个结点有且仅有唯一的一条路径连通。
一棵树如果有n个节点, 那么它一定有n-1条边。
在一棵树中添加一条边将会构成一个回路。
一棵有 n个节点的无向树,所有节点的度数和为2× (𝑛 − 1) 。

树形结构以树和二叉树最为常用,直观来看,树是以分支关系定义的层次结构。树形结构
中元素之间有着明显的层次关系,每一个元素可以和下层的多个元素相关, 但只能和上层
中一个元素相关。

1.3.树与链表

链表是一棵特殊的树。我们可以将链表的头节点看作树的根。树的每个节点可以有多个
next ,我们称之为子节点。所以链表是一棵每个节点都只有一个子节点的树。

链表
头节点 根结点
只有一个后继节点(next) 有一个或多个子节点(child)
双向链表包括前驱结点(pre) 除了根结点外,每个节点都有唯一的父节点(parent)
一对一关系 一对多关系

1.4.树的基本操作-树的存储与创建

在创建一棵树的时候, 使用什么方法去存储呢?
可以采用与链表类似的方法。但由于子节点的数量不确定, 因此我们想到用vector来存储
树的子节点。
在一般树中,子节点的数量没有限制,所以常用的存储方法是使用vector数组G, G[0]保
存编号为0的顶点连接到的所有顶点, 由于n个结点的树只有n-1条边, vector数组实际占
用的空间为O(n) 。
每读到一条边(u,v) ,如果不知道谁是父亲谁是儿子,我们可以先在G[u]中添加一个v ,再
在G[v]中添加一个u 。
如果知道父子关系,可以只在父节点中添加儿子,而不用将边保存两份。

vector<int> G[100005]; //每个vector用来记录所有子节点的编号
int n;
int main()
{
    scanf("%d", &n);
    for(int i = 1; i < n; i++){
    int u, v;
    scanf("%d %d",&u, &v);
    G[u].push_back(v);
    G[v].push_back(u);
    }
    return 0;
}

1.5.删除树的点和边

删除节点u时需要删除该节点的所有边。
Del(当前节点u){
    for(u有连边的节点v){
        从G[v]中删除u;
    }
    清空G[u];
}
删除边(u,v) 。
Del(边(u,v)){
    从G[v]中删除u;
    从G[u]中删除v;
}

1.6.树的遍历

遍历树的方式有很多,最常用的方式是DFS 。
DFS(当前节点u){
    for(u的所有子节点v){
        DFS(v);
    } 
}
如果是在不知父子的情况下,保存了双向边,那么需要做如下处理。
DFS(当前节点u, 父节点p){
    for(u有连边的节点v){
        if(v不是p)
            DFS(v, u);
    } 
}
遍历方式对比
数组 从下标0开始遍历
链表 从头节点开始遍历
从根节点开始递归遍历

1.7.例题:叶子节点的数量

给出一棵n个节点的树,节点编号为1-n(根节点编号为1),求这棵树叶子结点的数量。
例如:
1─2─4─5
└─3
其中3和5是叶子节点,输出2。
1.7.1.输入

第一行:1个数n(1 < n <= 1000),表示树的节点数量。
后面n-1行:每行2个数x y,表示节点x是节点y的父节点(1 <= x, y <= n)。

1.7.2.输出

输出1个数,表示这棵树有多少个叶子节点。

1.7.3.输入样例

5
1 2
1 3
2 4
4 5

1.7.4.输出样例

2

1.7.5.分析
可以用一个bool数组t[i]来存储每一个节点,t[i]表示第i个节点是否有子节点,如果没有,i就是叶子节点。但要注意输入 n-1次,判断t[i]是否为false时要循环n次。t数组要开在外边,否则在main函数里要memset初始化一下。
1.7.6.代码

#include <bits/stdc++.h>
using namespace std;
bool t[1010]; 
int main() {
    int n, ans = 0;
    cin >> n;
    for(int i = 1; i <= n - 1; i++) {
        int a, b;
        cin >> a >> b;
        t[a] = true;
    }
    for(int i = 1; i <= n; i++) if(!t[i]) ans++;
    cout << ans << endl;
    return 0;
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM