一:二叉树的遍历.
由于递归算法很简单,在这里就不例举了,主要看一下非递归算法(其实也就是用栈实现,因为递归本身就是一种栈)
1.先序遍历:
思想:(1)从根节点依次遍历当前节点的左子树,边遍历访问,并且压入栈
(2).再访问当前栈顶结点的右子树,然后再返回到(1)执行,直至栈空
#define maxsize 100
typedef struct
{
Bitree Elem[maxsize];
int base,top;
}SqStack;
void PreOrderUnrec(Bitree t)
{
SqStack s;
StackInit(s);
p=t;
while (p!=null || !StackEmpty(s))
{
while (p!=null) //遍历左子树
{
visite(p->data);
push(s,p);
p=p->lchild;
}//endwhile
if (!StackEmpty(s)) //通过下一次循环中的内嵌while实现右子树遍历
{
p=pop(s);
p=p->rchild;
}//endif
}//endwhile
}//PreOrderUnrec
2.中序遍历:
思想:(1)从根节点遍历左子树,边遍历边入栈
(2)弹出栈顶元素,并访问,然后访问当前栈顶的右子树,回到(1)
#define maxsize 100
typedef struct
{
Bitree Elem[maxsize];
int base,top;
}SqStack;
void InOrderUnrec(Bitree t)
{
SqStack s;
StackInit(s);
p=t;
while (p!=null || !StackEmpty(s))
{
while (p!=null) //遍历左子树
{
push(s,p);
p=p->lchild;
}//endwhile
if (!StackEmpty(s))
{
p=pop(s);
visite(p->data); //访问根结点
p=p->rchild; //通过下一次循环实现右子树遍历
}//endif
}//endwhile
}//InOrderUnrec
3.后序遍历
在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点。对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问,因此其右孩子还为被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是否是第一次出现在栈顶
while(p!=NULL||!s.empty())
{
while(p!=NULL) //沿左子树一直往下搜索,直至出现没有左子树的结点
{
BTNode *btn=(BTNode *)malloc(sizeof(BTNode));
btn->btnode=p;
btn->isFirst=true;
s.push(btn);
p=p->lchild;
}
if(!s.empty())
{
temp=s.top();
s.pop();
if(temp->isFirst==true) //表示是第一次出现在栈顶
{
temp->isFirst=false;
s.push(temp);
p=temp->btnode->rchild;
}
else //第二次出现在栈顶
{
cout<<temp->btnode->data<<" ";
p=NULL;
}
}
}
二.线索二叉树:
含有n个结点的二叉树,一共有2n个指针域,有n+1个处于Null状态,为了使空间不浪费,可以让这些空的指针域指向二叉树各种遍历的前驱或后继结点,这样又可以方便查找每一个元素,而不必采用遍历,节省了时间
1.存储结构:
typedef enum { Link, Thread } PointerThr;
// Link==0:指针,Thread==1:线索
typedef struct BiThrNode{
TElemType data;
Struct BiThrNode *lchild, *rchild; // 左右孩子指针
PointerThr LTag, RTag; // 左右标志,当LTag=Thread时,表示线索,为Link时表示指向下一结点
} BiThrNode, *BiThrTree;
1)若结点有左子树,则lchild指向其左孩子;
否则, lchild指向其直接前驱(即线索);
2).若结点有右子树,则rchild指向其右孩子;
否则,rchild指向其后继(即线索);
例:
2.线索二叉树的中序遍历算法:
Status IOTraver_T( BiThrTree T,Status (*Visit)(TElemType e) )
{ //T指向头结点,头结点的左链lchild指向根结点,中序遍历 //二叉线索树T的非递归算法,对每个数据元素调用函数Visit。
p = T->lchild; //p指向根结点
while (p != T) { //空树或遍历结束时,p = = T
while (p->LTag==Link) p = p->lchild;
if (!Visit(p->data)) return ERROR; //访问其左子树为空的结点
while (p->RTag==Thread && p->rchild!=T)
{ p = p->rchild; Visit(p->data); } //访问后继结点
p = p->rchild;
}
return OK;
} // IOTraver_T
3.线索二叉树的生成算法:
void InThreading (BiThrTree p)//中序并线索化
{
if (p)
{
InThreading( p->lchild ); // 左子树线索化
if ( !p->lchild )
{ p->LTag=Thread; p->lchild=pre; } // 前驱线索
if ( !pre->rchild )
{ pre->RTag=Thread; pre->rchild=p; } //后继线索
pre = p; // 保持pre指向p的前驱
InThreading(p->rchild); //右子树线索化
}
} // InThreading
Status InorderThreading(BiThrTree & Thrt, BiThrTree T)
{ //中序遍历二叉树T,并将其中序线索化, Thrt 指向头结点.
if ( ! (Thrt = (BiThrTree) malloc ( sizeof (BiThrnode) ) ) exit ( OVERFLOW ) ;
Thrt ->LTag = Link; Thrt ->RTag = Thead; // 建头结点
Thrt ->rchild = Thrt ; //右指针回指
if ( !T ) Thrt ->lchild = Thrt ; // 若二叉树空,则左指针回指
else {
Thrt ->lchild = T; pre = Thrt; //将头结点与树相连
<strong>InThreading(T); </strong> // 中序遍历进行中序线索化,调用上面的函数
pre ->rchild = Thrt;
pre ->RTag = Thread; //最后一个结点线索化
Thrt ->rchild = pre;
}
return OK;
} // InOrderThreading