(三十五)树与存储

1. 什么是树

树是一种数据类型,小区、街道、家乡都有。确实,它有一个特点:树枝永远不会重新会合。
树由许多个节点构成,每个节点会引出其他的节点,其他的节点无法汇合。也有节点后面不再引出节点,这种节点叫做叶子。树的起点叫做根节点,其他的叫做子节点。
 ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=!%5B%E5%9C%A8%E8%BF%99%E9%87%8C%E6%8F%92%E5%85%A5%E5%9B%BE%E7%89%87%E6%8F%8F%E8%BF%B0%5D(https%3A%2F%2Fi-blog.csdnimg.cn%2Fdirect%2F80a5724993d7462db3043fb6b37adf32.png&pos_id=img-MFNqGqnh-1737982554568这是一棵树
在这里插入图片描述 这不是一棵树,因为D和E相连了

2. 经典二叉树

2.1 二叉树的定义

二叉树,顾名思义就是任意一个节点最多只有两个子节点的树,其中自己左下方的节点叫左孩子,右下方的节点叫右孩子

2.2 完全二叉树

叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大,这种二叉树叫做完全二叉树。
在这里插入图片描述

2.3 满二叉树

满二叉树确保了每一层节点数都是最多的
在这里插入图片描述

2.4 存储方式

上图有以下存储方式

2.4.1 二维存储方式

从根节点开始,将节点存入 t r e e 0 , 0 tree_{0, 0} tree0,0 的位置
T r e e i , j 的左孩子位置 = T r e e i + 1 , j ∗ 2 Tree_{i, j}的左孩子位置=Tree_{i+1, j*2} Treei,j的左孩子位置=Treei+1,j2
T r e e i , j 的右孩子位置 = T r e e i + 1 , j ∗ 2 + 1 Tree_{i, j}的右孩子位置=Tree_{i+1, j*2+1} Treei,j的右孩子位置=Treei+1,j2+1
T r e e i , j 的父亲位置 = T r e e i − 1 , ⌊ j ÷ 2 ⌋ Tree_{i, j}的父亲位置=Tree_{i-1, \left \lfloor j\div2 \right \rfloor} Treei,j的父亲位置=Treei1,j÷2

Tree01234567j
0A
1BC
2DEFG
3HIJKLMNO
i

但这种方式耗内存,不太建议

2.4.2 一维存储方式

从根节点开始,将根节点存入第1项
左孩子位置 = 当前节点 × 2 左孩子位置=当前节点\times2 左孩子位置=当前节点×2
右孩子位置 = 当前节点 × 2 + 1 右孩子位置=当前节点\times2+1 右孩子位置=当前节点×2+1
父亲位置 = ⌊ 当前节点 ÷ 2 ⌋ 父亲位置=\left \lfloor当前节点\div2 \right \rfloor 父亲位置=当前节点÷2

i123456789101112131415
T r e e i Tree_i TreeiABCDEFGFIJKLMNO

2.5 节点计算公式

二叉树第 h 层最大节点个数 = 2 h − 1 二叉树第h层最大节点个数=2^{h-1} 二叉树第h层最大节点个数=2h1
二叉树截止第 h 层最大总节点数 = 2 h − 1 二叉树截止第h层最大总节点数=2^h-1 二叉树截止第h层最大总节点数=2h1

3. 二叉树

3.1 存储方式

在这里插入图片描述

3.1.1 父亲表示法

使用一个数组 F F F ,其中 F [ i ] F[i] F[i] 等于自己的父亲编号

iABCDEFGHIJKLMNO
T r e e i Tree_i Treei-AABBCCDDEEFFGG

注意现实的题目一般不会使用字符作为节点名称

以下代码先输入两个数字 n n n m m m ,表示有n个节点,m条边
接下来m行,每行两个数 v v v w w w ,表示节点v和节点w连着一条边( v , w ≠ 0 v, w \neq 0 v,w=0)
再输入一个数字 x x x ,从节点x开始往节点1遍历
输出一串数字,表示从节点x到节点1的路程

#include <bits/stdc++.h>
using namespace std; 

const int N = 100050; // 可替换为节点最大值
int F[N]; 
int v, w; 
int n, m, x; 

int main () {
	cin >> n >> m; 
	for(int i=1; i<=m; i++) {
		cin >> v >> w; 
		F[w] = v; 
	}
	cin >> x; 
	cout << x; 
	while(x!=1) {
		x = F[x]; 
		cout << "->" << x; 
	}
	return 0; 
}

3.1.2 儿子表示法

使用一个二维数组 G G G ,其中每一项表示它的儿子
A : B   C A: B~C A:B C
B : D   E B: D~E B:D E
C : F   G C: F~G C:F G
D : H   I D: H~I D:H I
E : J   K E: J~K E:J K
F : L   M F: L~M F:L M
G : N   O G: N~O G:N O
. . . 其余为空 ...其余为空 ...其余为空

以下代码先输入两个数字 n n n m m m ,表示有n个节点,m条边
接下来m行,每行两个数 v v v w w w ,表示节点v和节点w连着一条边( v , w ≠ 0 v, w \neq 0 v,w=0)
最终输出几行数字,表示从1往下遍历的路径
以这张图片为例
在这里插入图片描述
输入:

9 8
1 2
1 3
2 4
2 5
3 6
3 7
4 8
4 9

最终输出:

1->2->4->8
1->2->4->9
1->2->5
1->3->6
1->3->7
#include <bits/stdc++.h>

using namespace std; 

const int N = 1050; // 根据 n 的大小设定 
int v, w; 
int n, m; 
vector<int> G[N]; 
bool vis[N]; // vis[i] 表示第 i 个节点是否被遍历过 
vector<int> res; // 表示当前路径 

void traverse (int x) {
	if(G[x].empty()) { //表示已经无路可走了 
		for(auto it : res) {
			cout << it << "->"; 
		}
		cout << x << endl; 
		return; // 切记return,否则后面会继续遍历的!!! 
	}
	res.push_back(x); 
	for(auto it : G[x]) {
		if(!vis[it]) {
			vis[it] = 1; 
			traverse(it); 
		}
	}
	res.pop_back(); 
}

int main () {
	cin >> n >> m; 
	for(int i=1; i<=m; i++) {
		cin >> v >> w; 
		G[v].push_back(w); 
	}
	vis[1] = 1; 
	traverse(1); 
	return 0; 
}

3. 目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值