算法笔记–二叉树和线索二叉树
二叉树
链式存储结构:
typedef struct BitNode{
ElemType data; //数据域
struct BitNode *lchild,*rchild; //左右孩子指针
}BitNode,*BiTree;
二叉树的递归遍历
先序遍历
void PreOrder(BiTree T){
if(T!=NULL){
visit(T); //访问根节点
PreOrder(T->lchild); //递归遍历左子树
PreOrder(T->rchild); //递归遍历右子树
}
}
中序遍历
void InOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild); //递归遍历左子树
visit(T); //访问根节点
PreOrder(T->rchild); //递归遍历右子树
}
}
后续遍历
void PostOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild); //递归遍历左子树
PreOrder(T->rchild); //递归遍历右子树
visit(T); //访问根节点
}
}
上述三种遍历中,只需要改变访问根节点的顺序即可得到三种不同的遍历
二叉树的非递归遍历
非递归遍历二叉树需要用到栈
先序遍历
void PreOrder(BiTree T){
InitStack(S); //初始化一个栈
BiTree p=T; //p是遍历指针
while(p||IsEmpty(S)){ //栈不为空or节点p不为空时循环
if(p){ //一路向左
visit(p); //访问当前节点
push(p); //将其入栈
p=p->lchild; //将遍历指针p指向左孩子
}else{ //左孩子为空
Pop(s,p); //出栈
p=p->rchlid; //将遍历指针p指向右孩子
}
}
}
中序遍历
void InOrder(BiTree T){
InitStack(S); //初始化一个栈
BiTree p=T; //p是遍历指针
while(p||IsEmpty(S)){ //栈不为空or节点p不为空时循环
if(p){ //一路向左
push(p); //将其入栈
p=p->lchild; //将遍历指针p指向左孩子
}else{ //左孩子为空
pop(S,p); //出栈
visit(p); //访问当前节点
p=p->rchild; //将遍历指针p指向右孩子
}
}
}
后序遍历
void PostOrder(BiTree T){
InitStack(S);
BiTree p=T;
r=null;
while(p||IsEmpty(S)){
if(p){ //走到最左边
push(p);
p=p->lchild;
}else{ //向右走
GetTop(S,p); //读栈顶节点
if(p->rchild!=r&&p->rchild){ //若右子树存在且为访问过
p=p->rchild; //转向右边
}else{ //否则弹出节点并访问
pop(S,p);
visit(p);
r=p;//记录最近访问过的节点
p=null;//节点访问后,重置p指针
}
}
}
}
线索二叉树
存储结构描述如下
typedef struct ThreadNode(){
ElemType data;//数据元素
struct ThreadNode *lchild,*rchild;//左右孩子指针
int ltag,rtag;//左右线索标志
//ltag为0表示lchild指向节点左孩子
//ltag为1表示lchild指向节点的前驱
//rtag为0表示rchild指向节点右孩子
//rtag为1表示rchild指向节点的后继
}ThreadNode,*ThreadTree;
二叉树的线索化
通过先序遍历线索化二叉树
void PreThread(ThreadTree p,ThreadTree pre){
if(p){
if(!p->lchild){//p的左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(!pre->rchild&&pre){//pre的右子树为空且pre不为空,则建立pre的后继线索
pre->rchild=p;
pre->rtag=1;
}
pre=p;//标记当前节点成为刚刚访问过的节点
PreThread(p->lchild,pre);//递归,线索化左子树
PreThread(p->rchild,pre);//递归,线索化右子树
}
}
通过先序遍历建立先序线索二叉树的主过程算法如下
void CreatePreThreadTree(ThreadTree T){
ThreadTree *pre=null;
if(T){
PreThread(T,pre);
pre->rchild=null;//处理遍历的最后一个节点
pre->rtag=1;
}
}
通过中序遍历线索化二叉树
void InThread(ThreadTree p,ThreadTree pre){ //pre指向p的前驱
if(p){//p不为空
InThread(p->lchild,pre);//递归,线索化左子树
if(!p->lchild){//p的左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(!pre->rchild&&pre){//pre的右子树为空且pre不为空,则建立pre的后继线索
pre->rchild=p;
pre->rtag=1;
}
pre=p;//标记当前节点成为刚刚访问过的节点
InThread(p->rchild,pre);//递归,线索化右子树
}
}
通过中序遍历建立中序线索二叉树的主过程算法如下
void CreateInThreadTree(ThreadTree T){
ThreadTree *pre=null;
if(T){
InThread(T,pre);
pre->rchild=null;//处理遍历的最后一个节点
pre->rtag=1;
}
}
通过后序遍历线索化二叉树
void PostThread(ThreadTree p,ThreadTree pre){ //pre指向p的前驱
if(p){//p不为空
PostThread(p->lchild,pre);//递归,线索化左子树
PostThread(p->rchild,pre);//递归,线索化右子树
if(!p->lchild){//p的左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(!pre->rchild&&pre){//pre的右子树为空且pre不为空,则建立pre的后继线索
pre->rchild=p;
pre->rtag=1;
}
pre=p;//标记当前节点成为刚刚访问过的节点
}
}
通过后序遍历建立后序线索二叉树的主过程算法如下
void CreatePostThreadTree(ThreadTree T){
ThreadTree *pre=null;
if(T){
PostThread(T,pre);
pre->rchild=null;//处理遍历的最后一个节点
pre->rtag=1;
}
}
线索二叉树的遍历
中序线索二叉树的遍历
ThreadNode *FirstNode(ThreadNode *p){
while(p->ltag==0) p=p->lchild;
return p;
}
ThreadNode *NextNode(ThreadNode *p){
if(p->rtag==0) return FirstNode(p->rchild);
else return p->rchild; //r=1,直接返回后继
}
//不含头节点的中序线索二叉树的中序遍历
void InOrder(ThreadNode *T){
for(ThreadNode *p=FirstNode(T);p!=null;p=NextNode(p)){
visit(p);
}
}