一、树
- 树是用来按照层次关系组织一系列数据项的方式
- 有根树
- 树是特殊的图T = (V, E), 节点数|V| = n, 边数|E| = e
- 指定任一节点r ∈ V作为根后, T即称作有根树(rooted tree)
- 任何一组有根树,可以通过引入一个节点r和一组边的情况将它们连成一颗新的有根树,这个新的有根树的根就是r
- 对于新的有根树T,Ti称作以ri为根的子树(subtree rooted at ri), 记作Ti = subtree(ri)
- 有序树
- ri称作r的孩子(child), ri之间互称为兄弟(sibling)
- r为其父亲(parent), d = degree®为r的(出)度(degree)
- 可归纳证明: e = 所有节点的度数之和 = n - 1 = O(n)
- 故在衡量相关复杂度时,可以n作为参照
- 若指定Ti作为T的第i棵子树,ri作为r的第i个孩子,则称T作为有序树(ordered tree)
- 路径 + 环路
- V中的k + 1个节点,通过E中的k条边依次相联,构成一条路径(path),亦称通路
- 路径长度:边数 = k
- 环路(cycle / loop): vk = v0
- 连通加无环
- 节点之间均有路径,称作连通图(connected)
- 不含环路,称作无环图(acyclic)
- 树是无环连通图,极小连通图,极大无环图
- 故,任一节点v与根之间存在唯一路径
- 只要指定了根节点,每一个节点都将有一个唯一的指标,就是该节点到根节点的路径长度。可以根据该指标划分等价类。
- 深度 + 层次
- 不致歧义时,路径、节点和子树可相互指代。path(v) ~ v ~ subtree(v)
- v的深度:depth(v) = |path(v)|
- path(v)上的节点,均为v的祖先(ancestor),v是它们的后代(descendent)
- 其中,除自身以外,是真(proper)祖先/后代
- 半线性结构:v的祖先若存在则必然是唯一的,v的后继若存在则未必是唯一的
- 根节点是所有节点的公共祖先,深度为0
- 没有后代的节点称作叶子(leaf)
- 所有叶子深度中的最大者称作树/根的高度height(v) = height(subtree(v))
- 特别的,空树的高度取作-1
- depth(v) + height(v) <= height(T)
![树的结构示意](https://i-blog.csdnimg.cn/blog_migrate/d23949900e2896206d1a40d86941ea91.png#pic_center)
二、树的表示
- 接口
![树结构提供的接口](https://i-blog.csdnimg.cn/blog_migrate/198461846e29c0c02302c0d5eb65d1ee.png#pic_center)
- 父亲表示法
- 观察:除根外,任一节点有且仅有一个父节点
- 构思:将节点组织为序列,各节点分别记录,rank-秩,data-本身信息,parent-父节点的秩或位置。
- 空间性能:O(n)
- 时间性能:parent(): O(1); root(): O(n)或O(1); firstChild(): O(n);nextSibling(): O(n)
- 孩子节点表示法
- 构思:将节点组织为序列,各节点分别记录,rank-秩, data-本身信息, children-孩子列表,记录其所有的孩子节点
- 父亲孩子表示法
- 构思:将节点组织为序列,各节点分别记录,rank-秩,data-本身信息,parent-父亲节点,children-孩子列表
- 缺陷:每一个节点的children引用在规模上有可能相差极其悬殊,每一个小的数据集的长度都恰好是这个节点所对应的出度,而所有这些出度的总和就是整体的边数,等于n - 1.也就是平均而言,所有这些小的数据集的规模,也就是O(1)的样子,而我们这种组织方式,有时候要长达O(n)的一个数据集
- 长子 + 兄弟表示法
- 构思:每个节点均设两个引用,每个节点均指向其第一个孩子节点,第一个孩子节点指向其余的孩子节点纵:firstChild(); 横:nextSibling()
三、二叉树
- 定义:节点度数不超过2的树称作二叉树
- 同一节点的孩子和子树,均以左、右区分,隐含了树的有序性,左在前右在后。
- 基数:深度为k的节点,至多为2^k个
- 在含n个节点、高度为h的二叉树中h < n < 2^(h+1)
- n = h + 1时,退化为一条单链
- n = 2^(h+1) - 1时,即所谓的满二叉树(full binary tree)
- 二叉树的宽度长宽非常快,是指数型的;二叉树的高度长宽非常慢,是对数型的
- 真二叉树:每个节点的出度都是偶数,或者是0,或者是2,但是不能是1。如果节点出度为1,就在缺失的那一侧同样引入一个孩子节点,使之为2。在实际的算法中,这种添加完全是假想,不需要真的添加孩子节点,但可使算法的实现更加简洁。
- 如何通过二叉树描述一般意义的树
- 二叉树是多叉树的特例,但在有根且有序时,其描述能力却足以覆盖后者
- 转化方法:长子-兄弟表示法
四、二叉树的实现
#define BinNodePosi(T) BinNode<T>*
template <typename T> struct BinNode{
BinNodePosi(T) parent, lChild, rChild;
T data; int height; int size();
BinNodePosi(T) insertAsLC(T const &);
BinNodePosi(T) insertAsRC(T const &);
BinNodePosi(T) succ();
template <typename VST> void travLevel( VST &);
template <typename VST> void travPre(VST &);
template <typename VST> void travIn(VST &);
template <typename VST> void travPost(VST&);
}
template <typename T> BinNodePosi(T) BinNode<T>::insertAsLC(T const &e)
{return lChild = new BinNode(e, this);}
template <typename T> BinNodePosi(T) BinNode<T>::insertAsRC(T const &e)
{return rChild = new BinNode(e, this);}
template<typename T>
int BinNode<T>::size(){
int s = 1;
if(lChild) s += lChild->size();
if(rChild) s += rChild->size();
return s;
}
template <typename T> class BinTree{
protected:
int _size;
BinNodePosi(T) _root;
virtual int updateHeight(BinNodePosi(T) x);
void updateHeightAbove(BinNodePosi(T) x);
public:
int size() const {return _size;}
bool emoty() const {return !_root;}
BinNodePosi(T) root() const {return _root;}
}
#define stature(p) ((p) ? (p)->height : -1)
template <typename T>
int BinTree<T>::updateHeight(BinNodePosi(T) x){
return x->height = 1 + max(stature(x->lChild), stature(x->rChild));
}
template <typename T>
void BinTree<T>::updateHeightAbove(BinNodePosi(T) x){
while(x)
{updateHeight(x); x = x->parent;}
}
template<typename T> BinNodePosi(T)
BinTree<T>::insertAsRC( BinNodePosi(T) x, T const & e){
_size++; x->insertAsRC(e);
uodateHeightAbove(x);
return x->rChild;
}