【基础前置】树

【基础前置】树

T r e e Tree Tree

一棵树,一棵树
彼此孤离地兀立着
风与空气
告诉着他们的距离
但是在泥土的覆盖下
他们的根伸长着
在看不见的深处
他们把根须纠缠在一起
\quad \quad \quad \quad \quad \quad \quad \quad \quad \quad ——《树》艾青

树是什么?

树是 n n n ( n ⩾ 0 ) (n \geqslant0) (n0)有限结点 组成的一个具有 层次关系集合


有一些重要的概念(若无需要可以跳过)

概念

根节点
没有直接前驱,但有零个或多个直接后继的结点。
在这里插入图片描述
结点的度
一个结点的子树个数称为此结点的度。
在这里插入图片描述
如上图,结点1的度为3。
叶子结点
度为0的结点,即无后继的结点,也称为终端结点。
在这里插入图片描述
分支结点
度不为0的结点,也称为非终端结点。
在这里插入图片描述
孩子结点
一个结点的直接后继称为该结点的孩子结点。
在这里插入图片描述
图中圈出的结点为结点1的孩子结点。
双亲结点
一个结点的直接前驱称为该结点的双亲结点。
在这里插入图片描述
结点1为结点2、3、4的双亲结点。
兄弟结点
同一双亲结点的孩子结点之间互称兄弟结点。
在这里插入图片描述
祖先结点
一个结点的祖先结点是指从根结点到该结点的路径上的所有结点。
在这里插入图片描述
结点1、2、5是结点9的祖先。
子孙结点
一个结点的直接后继和间接后继称为该结点的子孙结点。
在这里插入图片描述
结点5、6、9、10是结点2的子孙节点。
树的度
树中所有结点的度的最大值。
如上面那棵树的度就为3。
结点的层次
从根结点开始定义,根结点的层次为1,根的直接后继的层次为2,依此类推。
树的高度(深度)
树中所有结点的层次的最大值。
森林
若干互不相交的树的集合。
二叉树
每个结点的度都不大于2且每个结点的孩子结点次序不能任意颠倒的树。
在这里插入图片描述
满二叉树
每层结点都具有最大结点数的二叉树。
在这里插入图片描述
完全二叉树
一棵深度为 k k k 的有 n n n 个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为 i ( 1 ⩽ i ⩽ n ) i(1\leqslant i\leqslant n) i1in的结点与满二叉树中编号为 i i i 的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
在这里插入图片描述

一些性质

性质1.1
描述: 在二叉树的第 i i i 层上至多有 2 i − 1 2^{i-1} 2i1个结点
证明:
i = 1 i=1 i=1 时,整个二叉树只有一根结点,此时 2 i − 1 = 2 0 = 1 2i-1=2^0=1 2i1=20=1,结论成立。
归纳假设:假设 i = k i=k i=k 时结论成立,即第 k k k 层上结点总数最多为 2 k − 1 2k-1 2k1 个。
现证明当 i = k + 1 i=k+1 i=k+1 时, 结论成立:
因为二叉树中每个结点的度最大为 2 2 2 ,则第 k + 1 k+1 k+1 层的结点总数最多为第 k k k 层上结点最大数的 2 2 2 倍,即 2 × 2 k − 1 = 2 ( k + 1 ) − 1 2×2^{k-1}=2^{(k+1)-1} 2×2k1=2(k+1)1 ,故结论成立。
性质1.2
描述: 深度为 k k k 的二叉树至多有 2 k − 1 2^k-1 2k1 个结点 ( k ⩾ 1 ) (k \geqslant 1) (k1)
证明:
设该二叉树第 i i i 层上有 n i n_i ni 个结点。
∵ n i ⩽ 2 i − 1 \because n_i \leqslant 2^{i-1} ni2i1(已证)
∴ ∑ i = 1 k n i ⩽ ∑ i = 1 k 2 i − 1 \therefore \sum_{i=1}^kn_i\leqslant\sum_{i=1}^k2^{i-1} i=1knii=1k2i1
∴ ∑ i = 1 k n i ⩽ 2 k − 1 \therefore \sum_{i=1}^kn_i\leqslant2^k-1 i=1kni2k1
性质1.3
描述: 对任意一棵二叉树 T T T,若终端结点数为 n 0 n_0 n0,而其度数为 2 2 2 的结点数为 n 2 n_2 n2,则 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1
证明:
T T T 中结点的度为 1 1 1 的结点数为 n 1 n_1 n1,总结点数为 n n n,则有
n 0 + n 1 + n 2 = n n_0+n_1+n_2=n n0+n1+n2=n
T T T 中共有 B B B 条边,则 B = n − 1 B = n-1 B=n1
由定义可知终端节点无出边,度数为 1 1 1 的结点有 1 1 1 条出边,度数为 2 2 2 的结点有 2 2 2 条出边,则
B = n 1 + 2 n 2 B = n_1+2n_2 B=n1+2n2
联立得
n 1 + 2 n 2 = n − 1 n_1+2n_2=n-1 n1+2n2=n1
n = n 0 + n 1 + n 2 n = n_0+n_1+n_2 n=n0+n1+n2 代入得
n 1 + 2 n 2 = n 0 + n 1 + n 2 − 1 n_1+2n_2=n_0+n_1+n_2-1 n1+2n2=n0+n1+n21
化简整理得 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1
性质1.4
描述: 具有 n n n 个结点的完全二叉树的深度为 ⌊ log ⁡ 2 n ⌋ + 1 \lfloor\log2^n\rfloor+1 log2n+1
证明:
n n n 个结点的完全二叉树的深度为 k k k,根据性质1.2可知, k − 1 k-1 k1层满二叉树的结点总数为
n 1 = 2 k − 1 − 1 n_1=2^{k-1}-1 n1=2k11
k k k 层满二叉树的结点总数为
n 2 = 2 k − 1 n_2=2^k-1 n2=2k1
显然有 n 1 < n ⩽ n 2 n1<n\leqslant n2 n1<nn2,进一步可以推出 n 1 + 1 ⩽ n < n 2 + 1 n1+1\leqslant n<n2+1 n1+1n<n2+1
n 1 = 2 k − 1 − 1 n_1=2^{k-1}-1 n1=2k11 n 2 = 2 k − 1 n2=2^k-1 n2=2k1代入上式,可得 2 k − 1 ⩽ n < 2 k 2^{k-1}\leqslant n<2^k 2k1n<2k,即 k − 1 ⩽ log ⁡ 2 n < k k-1\leqslant \log2n<k k1log2n<k
又因为 k k k 为整数,所以 k − 1 = ⌊ log ⁡ 2 n ⌋ k-1=\lfloor\log2n\rfloor k1=log2n k = ⌊ log ⁡ 2 n ⌋ + 1 k=\lfloor\log2n\rfloor+1 k=log2n+1, 故结论成立。

之后的树主要指的是二叉树

树怎么用?

存储

二叉树的存储结构有两种: 顺序存储结构链式存储结构

顺序存储结构
类似bfs保存的路径。
在这里插入图片描述
用顺序存储结构存下该二叉树:
在这里插入图片描述
现在似乎没问题。
但如果是一般的二叉树:
在这里插入图片描述
结果是:
在这里插入图片描述
其中 ^ 是空。
所以,当二叉树为非完全二叉树时,顺序存储结构会将空的子树进行存储,浪费大量空间。
链式存储结构
在每一个结点中存储该结点的数据信息,左子树(左孩子结点)信息,右子树(右孩子结点)信息。需要时还可以存储其他信息。
可以用链表存储,分为数据域,左子树域,右子树域。
在这里插入图片描述
但由于链表中的指针难以理解,因此可以用数组模拟链表。
在这里插入图片描述
由于有时树为非二叉树,孩子结点可以用数组储存。
遍历
常规的有前序遍历,中序遍历,后序遍历。
前序遍历也叫先根遍历,同理,中序遍历也叫中根遍历,后序遍历也叫后根遍历。
先序遍历一般指先遍历树的根,再遍历左子树、右子树。
在这里插入图片描述
路径:ABDGHCEIJF
中序遍历一般指先遍历左子树,再返回来遍历根,接着遍历右子树。
在这里插入图片描述
路径:GDHBAIECFJ
后序遍历一般指先遍历左子树,再遍历右子树,最后遍历根。
在这里插入图片描述
路径:GHDBIEJFCA

一些模板

非二叉树

树的存储

struct node {
	int data;
	int child[10];
	int father; // 父结点信息
} tree[1000005];

找根

int root; //根结点下标
for(int i = 1; i <= n; i ++) {
	if(!tree[i].father) { //没爸的就是根
		root = i;
		break;
	}
}
二叉树

存储

struct node {
	int data;
	int left_child;
	int right_child;
} tree[1000005];

找根

int root; 
for(int i = 1; i <= n; i ++) {
	if(!tree[i].father) { 
		root = i;
		break;
	}
}

前序遍历

void pre_order(int x) {
	if(!tree[x].data) { //虚点的概念
		return;
	}
	printf("%d ", tree[x].data); //根
	pre_order(tree[x].left_child); //左子树
	pre_order(tree[x].right_child); //右子树
}

中序遍历

void in_order(int x) { 
	if(!tree[x].data) {
		return;
	}
	in_order(tree[x].left_child);
	printf("%d ", tree[x].data);
	in_order(tree[x].right_child);
}

后序遍历

void post_order(int x) {
	if(!tree[x].data) {
		return;
	}
	post_order(tree[x].left_child);
	post_order(tree[x].right_child);
	printf("%d ", tree[x].data);
}

求二叉树深度

int Max = -1; //Max为深度
void dfs(int root, int deep) {
	if(!tree[root].child[0] && !tree[root].child[1]) { //为叶子结点
		Max = max(Max, deep);
	}
	if(a[root].child[0]) { //左子树不为空
		dfs(a[root].child[0], deep + 1);
	}
	if(a[root].child[1]) { //右子树不为空
		dfs(a[root].child[1], deep + 1);
	}
}

树有哪些?

平衡二叉树、排序二叉树、哈夫曼树、前缀字典树、后缀字典树、基数字典树、线段树、B树、红黑树、B+树、平衡二叉查找树、二叉堆、区间红黑树、B*树、2-3树、2-3-4树、四叉树、八叉树、K-D树、R树、R+树、R*树、伸展树、AAA树、李超线段树、主席树、划分树、替罪羊树、笛卡尔树、左偏红黑树、WBLT、珂朵莉树、LCT、ETT、析合树、PQ 树、手指树。

好大一棵二叉树
黄昏,暮霭漫漫
谁在寂寞地遍历树上每一个结点?
从无边的代码中抬起头
夜色,淹没了每一字节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值