数据结构目录
第一章 - 向量
第二章 - 列表
第三章 - 栈与队列
第四章 - 二叉树
第五章 - 图
第六章 - 搜索树
第七章 - 高级搜索树
第八章 - 词典
第九章 - 优先级队列
第十章 - 串
第十一章 - 排序
文章目录
一、树
兼具Vector和List的优点,兼顾高效的查找、插入、删除。
- 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
- 兄弟节点:具有相同父节点的节点互称为兄弟节点;
- 树的度:一棵树中,最大的节点的度称为树的度;
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
- 树的高度或深度:树中节点的最大层次;
二、树的表示
- 父亲节点
- 孩子节点
- 长子+兄弟
三、二叉树
遍历:按照某种次序访问树中各节点,每个节点被访问恰好一次。
假设X为节点,L为其左孩子,R为其右孩子,则访问次序:
X-L-R,为先序遍历(preorder)
L-X-R,为中序遍历(inorder)
L-R-X,为后序遍历(postorder)
1、先序遍历(算法A、递归算法)
2、先序遍历(算法B、非递归算法)
沿着左侧分支(藤),整个遍历过程可分解为自上而下对藤上节点的访问,以及随后自下而上对个右子树的遍历;各右子树的遍历彼此独立,自成一个子任务。
template <typename T, typename VST>
static void visitAlongVine(BinNodePosi(T) x, VST & visit, Stack <BinNodePosi(T)> & S){ //分摊O(1)
while ( x ) { //反复地
visit( x->data ); //访问当前节点
S.push( x->rc ); //右孩子(右子树)入栈(将来逆序出栈)
x = x->lc; //沿藤下行
} //只有右孩子、NULL可能入栈——增加判断以剔除后者,是否值得?
}
template <typename T, typename VST>
void travPre_I2(BinNodePosi(T) x, VST & visit){
Stack < BinNodePosi(T) > S; //辅助栈
while ( true ) { //以右子树为单位,逐批访问节点
visitAlongVine( x, visit, S ); //访问子树x的藤蔓,各右子树(根)入栈缓冲
if ( S.empty() ) break; //栈空即退出
x = S.pop(); //弹出下一右子树(根)
} //#pop = #push = #visit = O(n) = 分摊O(1)
}
3、中序遍历
沿着左侧分支(藤),整个遍历过程可分解为自底而上的d+1步迭代,访问藤上节点,再遍历其右子树;各右子树的遍历彼此独立,自成一个子任务。
4、中序遍历:前驱与后继
5、层次遍历
template <typename T> template <typename VST>
void BinNode::travLevel(VST &visit){ //二叉树层次遍历
Queue<BinNodePosi(T)> Q; //引入辅助队列
Q.enqueue(this); //根节点入队
while(!Q.empty()){ //在队列再次变空之前,反复迭代
BinNodePosi(T) x = Q.dequeue(); //取出队首节点,并随即
visit(x->data); //访问它
if(HasLChild(*x)) Q.enqueue(x->lc); //左孩子入队
if(HasLRhild(*x)) Q.enqueue(x->rc); //右孩子入队
}
}
6、Huffman编码
贪婪策略:频率低的字符优先引入,从而使其位置更低
为每个字符创建一棵单节点的树,组成森林F
按照出现频率,对所有树排序
while ( F中的树不止一棵 )
取出频率最小的两棵树:T1和T2
将它们合并成一棵新树T,并令:
lchild(T) = T1 且 rchild(T) = T2
w( root(T) ) = w( root(T1) ) + w( root(T2) )
7、后序遍历
从根出发下行,尽可能沿左分支,实不得已,才沿右分支,最后一个节点必是叶子,而且是按中序遍历次序最靠左者,也是递归版中visit()首次执行处,这匹叶子将首先接受访问。
8、重构(应用很少)
[先序|后序] + 中序,给出遍历的序列,就可以重构出二叉树。