树(Tree)是n(n>=0)个结点的有限集。
在任意一棵非空树中:
(1)有且仅有一个特定的称为根(Root)的结点;
(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,...,Tn,其中每个集合本身又是一棵树,并称为根的子树(SubTree)
层次 树
1 A
/ | \
2 B C D
/ \ | / | \
3 E F G H I J
/ \ |
4 K L M
★树的结点包含一个数据元素及若干指向其子树的分支。
(1)度
结点拥有的子树树称为结点的度(Degree)如:上图A的度为3,C的度为1,F的度为0。
度为0的结点称为叶子(Leaf)或终端结点,如:上图K,L,F,G,M,I,J都是树的叶子
度不为0的结点称为非终端结点或分支结点,如:上图A,B,C,D,E,H
★树的度是树内各节点的度的最大值,如:上图的树的度为 3 。
(2)结点(家谱图)
结点的子树的根称为该结点的孩子(Child),相应地,该结点称为孩子的双亲(Parent)如:上图D是A的孩子,A是D的双亲。
同一个双亲的孩子叫兄弟(Sibling)如:上图H,I,J为互为兄弟
其双亲在同一层的结点互为堂兄弟。如上图G与E、F、H、I、J互为堂兄弟
结点的祖先是从根到该结点所经分支上的所有结点。如:上图M的祖先为A、D、H
(3)层次,深度(你家几代同堂啊?)
结点的层次(Level)从根开始定义起,根为第一层,根的孩子为第二层,
树中结点的最大层次成为树的深度(Depth)或高度。如:上图树的深度为4
(4)有序树与无序树(长子,次子。。)
树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则成为无序树。
有序树中最左的子树的根称为第一个孩子,最右边的称为最后一个孩子。(毕竟有序,排好了谁是老大,谁是老二,长兄有序嘛)
★森林(Forest)是m(m>=0)棵互不相交的树的集合。对树中每个结点而言,其子树的集合即为森林。
由此也可以森林和树相互递归的定义来描述树。
1、二叉树的定义及其主要特征
二叉树(Binary Tree)是另一种树形结构,
★二叉树的特点是:(1)每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),
(2)二叉树的子树有左右之分,其次序不能随意颠倒。
二叉树要么为空,要么是由一个根结点加上两棵分别称为左子树和右子树的,互不相交的二叉树组成。
★二叉树的五种形态
| | A | A | A
Φ | A | B | B C | B
(1) | (2) | (3) | (4) | (5)
★二叉树的性质
(性质一)在二叉树的第 i 层上至多有 2^(i-1)个结点 (i >=1)
第一层 :1(2^0) 第二层:2(2^1) 第三层:4(2^2) 第四层:8(2^3)
(性质二)深度为 k 的二叉树至多有2^k -1个结点(k>=1)
由性质一得:第k层,2^k - 1 个结点
1+2+2^2+....+2^(k-1) = ( 1 - 2^k)/(1-2) = 2^k - 1
(性质三)★对任何一棵二叉树 T ,如果其终端结点数位n0,度为2的结点数位n2,则n0=n2+1。
式一: n = n0 + n1 + n2 (结点总数 等于 度为0 加 度为1 加 度为2)
式二: n = n0 + 2*n2 +1(n = 分支总数+1 ;分支总数 = n1+n2 (分支是由度为1,度为2的结点射出的))
式二 - 式一得: n0 = n2 + 1
★完全二叉树
一棵深度为 k 且有2^k - 1个结点的二叉树为满二叉树
(性质一)具有n个结点的完全二叉树的深度为 」 log 2 n」+1
由普通的二叉树性质二得:深度为k,结点数最多2^k - 1个,那么log一下就出来了
(性质二)如果对一棵有n个结点的完全二叉树(其深度为」 log 2 n」+1 )的结点按层序编号(从第一层到最后一层,每层从左到右),对任一结点i(1<= i <=n)有
(1)如果 i =1,则结点 i 是二叉树的根,无双亲;
如果 i >1,则其双亲Parent( i ) 是结点 」i / 2」
(2)如果2*i >n,则结点 i 无左孩子(结点 i 为叶子节点) ;否则其左孩子LChild( i )是结点2*i。
(3)如果2*i+1>n,则结点 i 无右孩子;否则其右孩子RChild( i )是结点2*i+1。
【这个性质,对于建立二叉树,有着非凡的意义】
且看下面这个完全二叉树:
1
/ \
2 3 [ i / 2 ]
/ \ / \ / \
4 5 6 7 i i+1
/ \ / \ / \ / \ / \ / \
8 9 10 11 12 13 14 15 2 i 2 i+1 2 i+2 2 i+3
【性质二通俗版】从下往上看:结点1是根,没双亲;其他点的双亲是 i / 2 取下值
从上往下看:左孩子 是根节点的两倍(偶数),右孩子是根节点的两倍+1(奇数)
2、二叉树的顺序存储结构和链式存储结构
顺序存储结构:
#define MAX_TREE_SIZE 100 //二叉树最大结点数
typedef TElemTyle SqBiTree[MAX_TREE_SIZE]; //0号单元存储根节点
SqBiTree bt;
[1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]
[1] [2] [3] [4] [5] [0] [0] [0] [0] [6] [7] [8]
链式存储结构
typedef struct BiTNode{
TElemType date;
struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree;
[ ] [A] [^]
[ ] [B] [ ]
/ \
[^] [C] [^] [ ] [D] [ ]
/ \
[^] [E] [ ] [^] [F] [^]
\
[^] [G] [^]
——————————————————————————————————————————————
【链式建树】【树形选择排序】【线段树——简单类型建树(int,char)】【线段树——结构体建树(详解推荐)】
——————————————————————————————————————————————
3、二叉树的遍历:传送门(建树,前序,中序,后序,中序非递归,深度,叶子数,节点数)
(1)先序遍历二叉树 (根>左>右)
(2)中序遍历二叉树 (左>根>右)
(3)后序遍历二叉树 (左>右>根)
(4)层次遍历二叉树
4、线索二叉树的基本概念和构造(传送门)
[ lchild ] [ LTag ] [ data ] [ RTag ] [ rchild ]
LTag = { 0 : lchild 域指示结点的左孩子 1 : lchild 域指示结点的前驱 }
RTag = { 0 : rchild 域指示结点的右孩子 2 : rchild 域指示结点的后继 }
以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中,指向结点前驱和后继的指针,叫做线索。
加上线索的二叉树叫做线索二叉树(Threaded Binary Tree)
对二叉树以某种次序遍历使其变成线索二叉树的过程叫做线索化
(1)前序线索二叉树
(2)中序线索二叉树
(3)后序线索二叉树
1、树的存储结构
A
↙ ↘
B C
↙ ↙ ↘
D E F
↙ ↓ ↘ ↘
G H I J
我们假设以一组连续空间存储树的结点,同时在每个结点中,附设一个指示器指示其双亲结点在数组中的位置。
每个结点:【data】【parent】
其中data:数据域,存储结点的数据信息。parent:指针域,存储该结点的双亲在数组找那个的下标。
#define MAX_TREE_SIZE 100
typedef int TElemType; //树节点的数据类型
typedef struct PTNode{
TElemType data; //结点数据
int parent; //双亲位置
}PTNode;
typedef struct{
PTNode nodes[MAX_TREE_SIZE]; //结点数组
int r,n; //根的位置和结点数
}PTree;
链式:
typedef struct PTrNode{
TElemType data; //结点数据
struct PTrNode *parent; //指向双亲的指针
}PTrNode;
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
data | A | B | C | D | E | F | G | H | I | J |
parent | -1 | 0 | 0 | 1 | 2 | 2 | 3 | 3 | 3 | 4 |
树中每个结点可能有多课子树,可以考虑用多重链表,即每个结点有多个指针域,其中每个指针指向一棵子树的根节点,这种方法叫做多重链表表示法。
方案一:
每个结点【data】【child1】【child2】【child3】....【childd】
其中data是数据域,child1~childd是指针域,用来指向该结点的孩子结点。指针个数为树的度数(每个结点的孩子结点个数为树的度,即最多大的孩子结点个数)
【缺点:这样很浪费空间】
方案二:
每个结点【data】【degree】【child1】【child2】【child3】....【childd】
其中data是数据域,degree是度域,child1~childd是指针域,用来指向该结点的孩子结点。
【这样就能减少空指针的浪费】
方案三:
用两种结点结构:
表头数组的表头节点:【data】【firstchild】其中:data是数据域,存储某结点的数据信息。firstchild是头指针域,存储孩子链表的头指针。
孩子链表的孩子结点:【child】【next】其中:child是数据域,存储某个节点在表头数组中的下标,next是指针域,指向下一个孩子结点的指针。
#define MAX_TREE_SIZE 100
typedef int TElemType; //树节点的数据类型
typedef struct CTNode{ //孩子结点
int child;
struct CTNode *next;
}*ChildPrt;
typedef struct{ //表头结构
TElemType data;
ChildPtr firstchild;
}CTBox;
typedef struct{ //树结构
CTBox nodes[MAX_TREE_SIZE]; //结点数组
int r,n; //根的位置和结点数
}CTree;
下标 data firstchild child next
0 【A】 【 】 → → → 【 1 】 【 】 → → → 【 2 】 【 ^ 】
1 【B】 【 】 → → → 【 3 】 【 ^ 】
2 【C】 【 】 → → → 【 4 】 【 】 → → → 【 5 】 【 ^ 】
3 【D】 【 】 → → → 【 3 】 【 】 → → → 【 7 】 【 】 → → 【 8 】 【 ^ 】
4 【E】 【 】 → → → 【 9 】 【 ^ 】
5 【F】 【 * 】
6 【G】 【 * 】
7 【H】 【 * 】
8 【I 】 【 * 】
9 【J】 【 * 】
下标 data parent firstchild child next
0 【A】 【 -1 】 【 】 → → → 【 1 】 【 】 → → → 【 2 】 【 ^ 】
1 【B】 【 0 】 【 】 → → → 【 3 】 【 ^ 】
2 【C】 【 0 】 【 】 → → → 【 4 】 【 】 → → → 【 5 】 【 ^ 】
3 【D】 【 1 】 【 】 → → → 【 3 】 【 】 → → → 【 7 】 【 】 → → 【 8 】 【 ^ 】
4 【E】 【 2 】 【 】 → → → 【 9 】 【 ^ 】
5 【F】 【 2 】 【 * 】
6 【G】 【 3 】 【 * 】
7 【H】 【 3 】 【 * 】
8 【I 】 【 3 】 【 * 】
9 【J】 【 4 】 【 * 】
结点结构:【data】【firstchild】【rightsib】
其中data为数据域,firstchild为指针域,存储该节点的第一个孩子结点存储地址,rightsub是指针域,存储该节点的右兄弟结点的存储地址。
typedef int TElemType; //树节点的数据类型
typedef struct CSNode{
TElemType data;
struct CSNode * firstchild,*rightsib;
}CSNode , *CSTree;
【 A 】【 】【 ^ 】
【 B 】【 】【 ^ 】 → → → → → → → → → → 【 C 】【 】【 ^ 】
↓ ↓
【 D 】【 】【 ^ 】 【 E 】【 】【 】 → 【 F 】【 ^ 】【 ^ 】
↓ ↓
【 G 】【 ^ 】【 】 → 【 H 】【 ^ 】【 】 → 【 I 】【 ^ 】【 ^ 】 【 J 】【 ^ 】【 ^ 】
斜着看,他其实是一棵二叉树。(根据代码也可以看出来)
根据二叉树的特性来处理树
2、森林和二叉树的转换
一、森林转换成二叉树
如果F={ T1,T2,....,Tm }是森林,则可按如下规则转换成一棵二叉树B=(root,LB,RB)
(1)若F为空,即m=0,则B为空树。
(2)若F非空,即m≠0,则B的根root即为森林中第一棵树的根ROOT(T1);
B的左子树LB是从T1中根结点的子树森林F1= { T11,T12,...,T1m1} 转换而成的二叉树
B的右子树RB是从森林F‘ = { T2,T3,.....,Tm}转换而来的二叉树。
二、二叉树转换成森林
如果B=(root,LB,RB)是一棵二叉树,则可按如下规则转换成森林F={ T1,T2,....,Tm }
(1)若B为空,则F为空。
(2)若B非空,则F中第一棵树T1的根root即为二叉树B的根root;
T1中根结点的子树森林F1是由B的左子树LB转换而成的森林;
F中除T1之外其余树组成的森林F‘ = { T2,T3,.....,Tm}是由B的右子树RB转换而成的森林。
3、树与森林的遍历
一、先序遍历森林
若森林非空,则可按下述规则遍历之;
(1)访问森林中第一棵树的根结点;
(2)先序遍历第一棵树中根结点的子树森林;
(3)先序遍历除去第一棵树之后剩余的树构成的森林。
二、中序遍历森林
若森林非空,则可按下述规律遍历之;
(1)中序遍历森林中第一棵树的根结点的子树森林;
(2)访问第一棵树的根结点;
(3)中序遍历除去第一棵树之后剩余的树构成的森林。
————————————————————
【并查集建树(利用数组的一对多特性)双亲表示法】
————————————————————
1、二叉排序树
2、平衡二叉树
3、哈夫曼树和哈夫曼编码。