这是我参与更文挑战的第16天,活动详情查看:更文挑战
树
二叉树的遍历与线索二叉树
二叉树的遍历
- 先序遍历: 若二叉树不为空,
- 访问根结点
- 先序遍历左子树
- 先序遍历右子树
- 中序遍历: 若二叉树不为空,
- 中序遍历左子树
- 访问根节点
- 中序遍历右子树
- 后序遍历: 若二叉树不为空,
- 后序遍历左子树
- 后序遍历右子树
- 访问根节点
ps.其实就是左子树在前右子树在后,然后根节点在前面就是先序,在中间就是中序
- 递归算法与非递归算法的转换
以中序为例
void InOrder2(BiTree T)}{
//二叉树中序遍历的非递归算法,算法需要借助一个栈
InitStack(S);BiTree *p = T;// 初始化栈,p是遍历指针
while(p|| !isEmpty(S)){ //如果p非空或者S非空
if(p){
push(S,p);
p = p->lchild;
}else{
Pop(S,p);
visit(p);
p=p->prchild;
}
}
}
-
层次遍历
-
由遍历序列构造二叉树
无法通过先序序列和后序序列确定唯一二叉树
线索二叉树
传统的链式存储仅能体现一种父子关系,不能直接得到结点在遍历中的前驱和后继。二叉链表表示的二叉树存在大量的空指针,可以利用其构建结点的前驱和后继。
前文提到,在n个结点的二叉树有n个空指针
ltag | lchild | data | rchild | rtag |
---|---|---|---|---|
0:左孩子,1:前驱 | 0:右孩子,1:后继 |
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
线索二叉树的构造
// 中序遍历对二叉树线索化
void InThread(ThreadTree &p.ThreadTree &pre){
if(p!=null){
InThread(p->lchild,pre); // 递归线索化左子树
if(p->lchild == NULL){ // 左子树为空,建立前驱线索
p->lchild = pre;
p->ltag = 1;
}
if(pre!=NULL&&pre->rchild ==NULL){
pre->rchild =p; // 建立前驱结点的后继线索
pre->rtag = 1;
}
pre = p;
InThread(p->rchild,pre);
}
}
void CreateInThread(ThreadTree T){
ThreadTree pre = NULL;
if(T!=NULL){
InThread(T,pre);
pre->rchild = NULL;
pre->rtag = 1;
}
}
可以设置一个头结点,令其lchild域的指针指向二叉树的根节点,令其rchild域的指针指向中序遍历时访问的最后一个结点。同时,将二叉树中序序列的第一个结点lchild域的指针和最后一个结点的rchild域的指针均指向头结点。使二叉树可以两个方向的遍历
二叉树的遍历
//求中序线索二叉树中中序序列下的第一个结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag == 0)p=p->lchild; //不一定是叶子结点
return p;
}
// 求中序线索二叉树中结点p在中序序列下的后继结点
ThreadNode *Nextnode(ThreadNode *p){
if(p->tag == 0) return Firstnode(p->rchild);
else return p->rchild;
}
// 中序遍历
void Inorder(ThreadNode *T){
for(ThreadNode *p = Firstnode(T);p!=NULL;p=Nextnode(p))
visit(p);
}