二叉树一般多采用二叉链表存储,其基本思想是:令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子结点的指针。二叉链表的结点结构如下图所示,其中data是数据域,lchild是左指针域,rchild是右指针域;当左右孩子都不存在时,左右指针都为空。
用C++的结构体类型描述二叉树链表的结点:
template<class Type>
struct BiNode{
Type data; //数据域
BiNode<Type> *lchild, *rchild; //指向左孩子和右孩子结点的指针
};
一、二叉树的遍历
遍历是二叉树最基本的操作。二叉树的遍历是指从根节点出发,按照某种次序访问二叉树中的所有结点,使得每个结点被访问且仅被访问一次。由于二叉树中每个结点都有可能有两棵子树,因而需要寻找一条合适的搜索路径。
由二叉树的定义可以知道,一棵二叉树由根结点、根结点的左子树和根结点的右子树三部分组成。因此只要一次遍历这三部分就可以遍历整个二叉树了。
前序、中序、后序遍历用递归实现,层次遍历通过队列来辅助实现。
1.前序遍历
思路:
若二叉树为空,则空操作返回,否则:
(1)访问根结点
(2)前序遍历根结点的左子树
(3)前序遍历根结点的右子树
template<class Type>
void BiTree<Type>::PreOrder(BiNode<Type>* bt){
if (bt == NULL)
return;
else{
cout << bt->data << " ";
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
}
2.中序遍历
思路:
若二叉树为空,则空操作返回,否则:
(1)中序遍历根结点的左子树
(2)访问根结点
(3)中序遍历根结点的右子树
template<class Type>
void BiTree<Type>::InOrder(BiNode<Type>* bt){
if (bt == NULL)
return;
else{
InOrder(bt->lchild);
cout << bt->data << " ";
InOrder(bt->rchild);
}
}
3.后序遍历
思路:
若二叉树为空,则空操作返回,否则:
(1)后序遍历根结点的左子树
(2)后序遍历根结点的右子树
(3)访问根结点
template <class Type>
void BiTree<Type>::PostOrder(BiNode<Type>* bt){
if (bt == NULL)
return;
else{
PostOrder(bt->lchild);
PostOrder(bt->rchild);
cout << bt->data << " ";
}
}
4.层次遍历
在进行层次遍历时,对某一层的结点访问完后,再按照它们的访问次序对各个结点的左孩子和右孩子顺序访问,这样一层一层进行,先访问的结点其左孩子也要先访问,这符合队列的操作特性。
思路:
(1)队列Q初始化
(2)如果二叉树非空,则根指针入队列
(3)循环直到队列Q为空
3.1 q是取的队头元素
3.2 访问q的数据域
3.3 若q存在左孩子,则将左孩子指针入队列
3.4 若q存在右孩子,则将右孩子指针入队列
template<class Type>
void BiTree<Type>::LevelOrder(){
SeqQueue<Type> sq;
if (root == NULL)return;
else{
sq.EnQueue(root);
while (!sq.isEmpty()){
BiNode<Type>* p = new BiNode<Type>;
p = sq.DeQueue();
cout << p->data << " ";
if (p->lchild != NULL)
sq.EnQueue(p->lchild);
if (p->rchild != NULL)
sq.EnQueue(p->rchild);
}
}
}
二、创建二叉树
构造函数的功能是建立一个二叉树,建立二叉树可以有多种方式,一个较为简单的方法就是根据一个结点序列来建立二叉树。由于前序、中序、后序序列中任何一个都不能唯一确定一个二叉树,因此不能直接使用。三种序列不能唯一确定二叉树的原因是:不能确定其左右子树的情况。针对以上问题,可以对二叉树做如下处理:将二叉树的每个结点的空指针引出一个虚结点,其值为以特定值,如#,以表示其为空。把这样处理后的二叉树称为原二叉树的扩展二叉树。
为简化问题,这里设二叉树中的结点均是一个字符。假设扩展二叉树的前序遍历序列由键盘输入,建立过程&#