概念:
n(n>=0) 个节点构成的有限集合
- 空树
- n = 0
- 非空树
- 有一个根节点
- 其余节点分为m(m>0)个互不相交的有限集,每个集合本身也是树
非树:
树:
特点:
- 子树不相交
- 除了根节点外,每个节点有且仅有一个父节点
- n个节点的树有n-1条边
表示方式:
- 树形表示法
- 文氏图表示法
- 凹入表示法
- 括号表示法
以存储方式的图为例子:A(B(E(K,L),F),C(G),D(H(M),I,J))
基本术语:
- 节点的度(Degree):一个节点的子节点的个数
- 树的度:树中,最大的节点的度
- 叶节点(Leaf):度为0的节点
- 父节点(Parent):有子树的节点,且是子树的的根节点
- 子节点(Child):A是B的父节点,则B是A的子节点
- 兄弟节点(Sibling):具有同一父节点的各节点彼此是兄弟节点
- 路径:从节点n到nk的路径为一个节点序列
- 路径长度:路径中所包含边的个数为路径长度
- 祖先节点(Ancestor):沿树根到某个节点上的所有节点都是这个节点的祖先节点
- 子孙节点(Descendant):某一节点的子树中所有节点是这个节点的子孙
- 节点的层次(Level):规定根节点在1层,其他任意节点是其分节点的层数+1
- 树的深度(Depth):树中所有节点中的最大层次是这颗树的深度
性质:
-
树中节点数 等于 所有节点的度数之和 + 1
下图中每个数字代表此节点的度,即 3 + 2 + 1 + 1(根节点) = 7
-
度为m的树中第i层上最多有mi-1 (i >=1)个节点
度为m的树则至少一个节点有m个子节点,最多则是每个节点下有m个子节点
从
-
高度为i的次树最多有 m i − 1 m − 1 \frac{m ^i - 1 }{m-1} m−1mi−1 个节点
也就是每层节点相加Sn = 1 + m1 + m2 + … + mi-1 (1)
再简化:
m * Sn = m + m2 + m3 + … + mi (2)
(2) - (1) : (m - 1) Sn= mi - 1
∴ \therefore ∴ Sn = m i − 1 m − 1 \frac{m ^i -1 }{m-1} m−1mi−1 -
具有n个节点的m次树的最小高度为 ⌈ l o g m ( n ( m − 1 ) + 1 ) ⌉ \left\lceil\\log_m(n(m - 1) +1)\right\rceil ⌈logm(n(m−1)+1)⌉
最小高度即每个节点的度最好都要到 m 个,才能保证最小高度
假设n个节点m次树的高度为i,假设前 i-1 层都是满的,那么 i 层就可能满,但也能不满
m i − 1 − 1 m − 1 + 1 ≤ n ≤ m i − 1 m − 1 \frac{m ^{i-1}-1 }{m-1} +1 \leq n \leq \frac{m ^ i -1 }{m-1} m−1mi−1−1+1≤n≤m−1mi−1
简化:
方便计算简化: m i − 1 − 1 m − 1 < n ≤ m i − 1 m − 1 \frac{m ^{i-1} -1}{m-1} < n \leq \frac{m ^i-1 }{m-1} m−1mi−1−1<n≤m−1mi−1
均乘以(m - 1): m i − 1 < n ( m − 1 ) + 1 ≤ m i m^{i-1} < n(m-1) +1 \leq m^i mi−1<n(m−1)+1≤mi
取m为底对数: i − 1 < l o g m ( n ( m − 1 ) + 1 ) ≤ i i - 1 <log_m(n(m-1)+1) \leq i i−1<logm(n(m−1)+1)≤i
∴ \therefore ∴ l o g m ( n ( m − 1 ) + 1 ) ≤ i < l o g m ( n ( m − 1 ) + 1 ) + 1 log_m(n(m-1) + 1) \leq i <log_m(n(m-1)+1) +1 logm(n(m−1)+1)≤i<logm(n(m−1)+1)+1
∵ \because ∵ 高度i只能去整数
∴ \therefore ∴ i = ⌈ l o g m ( n ( m − 1 ) + 1 ) ⌉ \left\lceil\\log_m(n(m - 1) +1)\right\rceil ⌈logm(n(m−1)+1)⌉
遍历:
- 先根遍历:
(1). 访问根节点
(2). 从左往右的顺序先根遍历所有子节点
以存储方式的图为例子:A B E K L F C G D H M I J - 后根遍历:
(1). 从左往右的顺序后根遍历所有子节点
(2). 访问根节点
以存储方式的图为例子:K L E F B G C M H I J D A - 层次遍历:
从根节点从上往下, 从左往右访问节点
以存储方式的图为例子:A B C D E F G H I J K L M
存储方式:
- 数组:
通常的树很难用数组表示(二叉树除外) - 链表:
这种方式构建链表,发现链表中的指针数量是根据整个树的度来决定的,当前树的度是3,也就是每个节点都有3个指针,如果一个节点中没有子节点,那么他会浪费会浪费很多空间(孩子链存储方式)
优化:儿子-兄弟表示法(类似于二叉树)
也就是左边放置子节点,右边放置兄弟节点
简单的实现:
根据给的节点
{
(‘A’, ‘B’),(‘A’, ‘C’),(‘A’, ‘D’)
(‘B’, ‘E’),(‘B’, ‘F’)
(‘C’, ‘G’)
(‘D’, ‘H’),(‘D’, ‘I’),(‘D’, ‘J’)
(‘E’, ‘K’),(‘E’, ‘L’)
(‘H’, ‘M’)
}
注:下面实现不包括值相同的节点,即B->B,这种操作不行,需要添加标志,注明节点的顺序
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
// 节点结构
struct TreeNode
{
char value;
TreeNode* child;
TreeNode* nextBrother;
TreeNode(char val):value(val), child(NULL), nextBrother(NULL)
{}
};
// 树
class Tree
{
public:
TreeNode* root;
Tree() {}
~Tree();
void CreateTree(std::map<int, std::map<char, char>> data); // 创建树
void DestroyNode(TreeNode* node); // 销毁树
TreeNode* GetNodeByVal(TreeNode* node, char val); // 根据值获取节点
};
void Tree::CreateTree(map<int, map<char, char>> data)
{
TreeNode* node = NULL;
for (int i = 0;i < data.size();i++)
{
map<char, char>::iterator iter = data[i].begin();
node = GetNodeByVal(node, iter->first);
if ( node == NULL)
{
node = new TreeNode(iter->first);
if (i == 0)
{
root = node;
}
}
if (node->child == NULL)
{
node->child = new TreeNode(iter->second);
}
else
{
TreeNode** next = &(node->child->nextBrother);
while (*next != NULL)
{
next = &((*next)->nextBrother);
}
*next = new TreeNode(iter->second);
}
}
}
TreeNode* Tree::GetNodeByVal(TreeNode* node, char val)
{
if (node == NULL || (node!=NULL && node->value == val))
{
return node;
}
TreeNode* cNode = GetNodeByVal(node->child, val);
if (cNode != NULL)
{
return cNode;
}
else
{
TreeNode* bNode = GetNodeByVal(node->nextBrother, val);
return bNode;
}
}
Tree::~Tree()
{
DestroyNode(root);
}
void Tree::DestroyNode(TreeNode* node)
{
TreeNode* child = NULL;
TreeNode * brother = NULL;
if (node == NULL)
{
return;
}
child = node->child;
brother = node->nextBrother;
delete node;
node = NULL;
DestroyNode(child);
DestroyNode(brother);
}
int main()
{
map<int, map<char, char>> data;
data[0].insert(pair<char, char>('A', 'B'));
data[1].insert(pair<char, char>('A', 'C'));
data[2].insert(pair<char, char>('A', 'D'));
data[3].insert(pair<char, char>('B', 'E'));
data[4].insert(pair<char, char>('B', 'F'));
data[5].insert(pair<char, char>('C', 'G'));
data[6].insert(pair<char, char>('D', 'H'));
data[7].insert(pair<char, char>('D', 'I'));
data[8].insert(pair<char, char>('D', 'J'));
data[9].insert(pair<char, char>('E', 'K'));
data[10].insert(pair<char, char>('E', 'L'));
data[11].insert(pair<char, char>('H', 'M'));
Tree t;
t.CreateTree(data);
// t.DestroyNode(t.root); 测试是否删除
system("pause");
return 0;
}