定义两种结构体:
树的节点
(数据域:字符 )
(指针域:指向自身类型的左指针,指向自身类型的右指针)
列队的节点
(数据域:指向树的节点的指针 )
( 指针域:指向自身类型的指针)
typedef struct BiTNode //树的节点
{
ElemType c;
struct BiTNode *lchild;
struct BiTNode *rchild;
} BiTNode,*BiTree;
为了节省空间,辅助队列的节点,数据域只需要存放树某个节点的指针,而不需要存放完整的树节点
typedef struct tag //辅助链队的节点,为了节省空间,数据域存放的是树某个节点的指针!!
{
BiTNode* p; //指向树节点类型的指针
struct tag* pnext; //指向自身类型的指针
} tag_t,*ptag_t;
1.首先第一个节点为根节点,入队
2.从第二个节点开始,首先入队。依次判断当前队头元素的左、右儿子是否为空,空就成为其左或右儿子,两个儿子都满了,辅助队列的遍历指针向后移动。
malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
BiTree pnew; //临时指向新的节点的工作指针,后面会赋值给树
char c; //暂存数据
BiTree tree=NULL; //树根
ptag_t phead=NULL;
ptag_t ptail=NULL; //辅助队列头
ptag_t listpnew=NULL; //ptail辅助队列尾
ptag_t pcur=NULL;
//abcdefghij
while(scanf("%c",&c)!=EOF) //对于每个输入的元素实时生成节点并加入树
{
if(c=='\n') break; //回车结束输入
//树的节点
pnew=(BiTree)calloc(1,sizeof(BiTree)); //创建一个新的树的节点,并赋值为0
pnew->c=c; //树的节点放入当前输入的数据
//队列的节点
listpnew=(ptag_t)calloc(1,sizeof(tag_t)); //创建(申请空间)辅助队列节点
listpnew->p=pnew; //队列节点存放的指向新节点 的指针
if(tree==NULL) //树的第一个节点加入
{
tree=pnew; //把树的新节点赋给树根
phead=listpnew; //辅助队列节点入队:辅助队列头指针,指向新的辅助队列节点
ptail=listpnew; //辅助队列尾指针,指向新的辅助队列节点
pcur=listpnew; //游标用来遍历队列,永远指向树中要插入位置的父节点
continue; //处理第二个和以后树的节点
}
else //当前不是第一个节点,加入已经存在的树
{
ptail->pnext= listpnew; //用尾插法把树的新节点加入辅助队列
ptail=listpnew;
}
//把新节点放到已有的树中
if(pcur->p->lchild==NULL) //判断树现在节点有没有左儿子
{
pcur->p->lchild=pnew; //在树现节点的左边插入新节点
}
else if(pcur->p->rchild==NULL) //如果树当前节点已经有左儿子,判断树现在节点有没有右儿子
{
pcur->p->rchild=pnew; //在树现节点的右边插入新节点
pcur=pcur->pnext; //!!有了右儿子,树这个节点已满,移动到辅助队列中下一个树节点
}
}
void preOrder(BiTree T) //递归前序遍历(DFS)
{
if(p!=NULL)
{
putchar(p->c);
preOrder(p->lchild);
preOrder(p->rchild);
}
}
void inOrder(BiTree T) //递归中序遍历
{
if(p!=NULL)
{
inOrder(p->lchild);
putchar(p->c);
inOrder(p->rchild);
}
}
void postOrder(BiTree T) //递归后序遍历
{
if(p!=NULL)
{
postOrder(p->lchild);
postOrder(p->rchild);
putchar(p->c);
}
}
使用了用户栈,相比递归遍历,效率提高了
void inOrder_NonRecursion(BiTree T) //非递归中序遍历,使用栈存放指向树节点的指针
{
if(T!=NULL)
{
stack<BiTree> S;
BiTree p=T; //指向树根的指针
while(p|| !S.empty()) //直到最右边的右儿子为空,且栈空,结束遍历
{
if(p) //当前节点不为空,先不断让当前节点入栈,并取当前节点的左孩子继续入栈,知道没有左孩子
{
S.push(p); //当前节点压栈,为后面能找到上一级的子树做准备
p=p->lchild; //p更新为当前节点的左孩子
}
else //直到没有左孩子,弹出栈中元素并打印,然后获取该元素的右节点
{
p=S.top(); //取出当前最左下的元素
S.pop( );
putchar(p->c); //弹栈并打印(访问当前子树的,中间节点)
p=p->rchild; //访问右子树,直到没有右孩子
}
}
}
}
void levelOrder(BiTree T) //按层次遍历(BFS),使用队列
{
queue<BiTree> Q; //建立队列
BiTree p; //二叉树节点类型的工作指针
Q.push(T); //树根入队
while(!Q.empty())
{
p=Q.front();
Q.pop(); //取队头节点并打印
putchar(p->c);
if(p->lchild!=NULL)
Q.push(p->lchild);
if(p->rchild!=NULL)
Q.push(p->rchild);
}
}
#include
using namespace std;
typedef char ElemType;
typedef struct BiTNode //树的节点
{
ElemType c;
struct BiTNode *lchild;
struct BiTNode *rchild;
} BiTNode,*BiTree;
typedef struct tag //辅助链队的节点,为了节省空间,数据域存放的是树某个节点的指针!!
{
BiTNode* p; //指向树节点类型的指针
struct tag* pnext; //指向自身类型的指针
} tag_t,*ptag_t;
void preOrder(BiTree p) //递归前序遍历(DFS)
{
if(p!=NULL)
{
putchar(p->c);
preOrder(p->lchild);
preOrder(p->rchild);
}
}
void inOrder(BiTree p) //递归中序遍历
{
if(p!=NULL)
{
inOrder(p->lchild);
putchar(p->c);
inOrder(p->rchild);
}
}
void postOrder(BiTree p) //递归后序遍历
{
if(p!=NULL)
{
postOrder(p->lchild);
postOrder(p->rchild);
putchar(p->c);
}
}
void preOrder_NonRecursion(BiTree T) //非递归先序遍历,使用栈存放指向树节点的指针
{
if(T!=NULL)
{
stack<BiTree> S;
BiTree p;
S.push(T);
while(!S.empty())
{
p=S.top();
S.pop();
putchar(p->c); //弹栈并打印
if(p->rchild!=NULL)
S.push(p->rchild);
if(p->lchild!=NULL)
S.push(p->lchild);
}
}
}
void inOrder_NonRecursion(BiTree T) //非递归中序遍历,使用栈存放指向树节点的指针
{
if(T!=NULL)
{
stack<BiTree> S;
BiTree p=T; //遍历指针,首先指向树根
while(p|| !S.empty()) //直到最右边的右儿子为空,且栈空,结束遍历
{
if(p) //一路向左压栈
{
S.push(p); //当前节点压栈
p=p->lchild; //左孩子不为空,一路向左
}
else //直到没有左孩子,弹出栈中元素并打印,然后转向出栈元素的右子树
{
p=S.top(); //取出当前最左下的元素
S.pop( );
putchar(p->c); //弹栈并打印(访问当前子树的,中间节点)
p=p->rchild; //向右子树走,对于右子树,从while循环再开始一轮
}
}
}
}
void postOrder_NonRecursion(BiTree T)//非递归后序遍历,使用两个栈
{
if(T!=NULL)
{
stack<BiTree> S1,S2;
S1.push(T);
BiTree p=NULL;
while(!S1.empty())
{
p=S1.top();
S1.pop( );
S2.push(p);
if(p->lchild!=NULL)
S1.push(p->lchild);
if(p->rchild!=NULL)
S1.push(p->rchild);
}
while(!S2.empty())
{
p=S2.top();
S2.pop( );
putchar(p->c); //弹栈并打印
}
}
}
void levelOrder(BiTree T) //按层次遍历(BFS),使用队列
{
queue<BiTree> Q; //建立队列
BiTree p; //二叉树节点类型的工作指针
Q.push(T); //树根入队
while(!Q.empty())
{
p=Q.front();
Q.pop(); //取队头节点并打印
putchar(p->c);
if(p->lchild!=NULL)
Q.push(p->lchild);
if(p->rchild!=NULL)
Q.push(p->rchild);
}
}
int main()
{
BiTree pnew; //临时指向新的节点的工作指针,后面会赋值给树
char c; //暂存数据
BiTree tree=NULL; //树根
ptag_t phead=NULL;
ptag_t ptail=NULL; //辅助队列头
ptag_t listpnew=NULL; //ptail辅助队列尾
ptag_t pcur=NULL;
//abcdefghij
while(scanf("%c",&c)!=EOF) //对于每个输入的元素实时生成节点并加入树
{
if(c=='\n') break; //回车结束输入
//树的节点
pnew=(BiTree)calloc(1,sizeof(BiTree)); //创建一个新的树的节点,并赋值为0
pnew->c=c; //树的节点放入当前输入的数据
//队列的节点
listpnew=(ptag_t)calloc(1,sizeof(tag_t)); //创建(申请空间)辅助队列节点
listpnew->p=pnew; //队列节点存放的指向新节点 的指针
if(tree==NULL) //树的第一个节点加入
{
tree=pnew; //把树的新节点赋给树根
phead=listpnew; //辅助队列节点入队:辅助队列头指针,指向新的辅助队列节点
ptail=listpnew; //辅助队列尾指针,指向新的辅助队列节点
pcur=listpnew; //游标用来遍历队列,永远指向树中要插入位置的父节点
continue; //处理第二个和以后树的节点
}
else //当前不是第一个节点,加入已经存在的树
{
ptail->pnext= listpnew; //用尾插法把树的新节点加入辅助队列
ptail=listpnew;
}
//把新节点放到已有的树中
if(pcur->p->lchild==NULL) //判断树现在节点有没有左儿子
{
pcur->p->lchild=pnew; //在树现节点的左边插入新节点
}
else if(pcur->p->rchild==NULL) //如果树当前节点已经有左儿子,判断树现在节点有没有右儿子
{
pcur->p->rchild=pnew; //在树现节点的右边插入新节点
pcur=pcur->pnext; //!!有了右儿子,树这个节点已满,移动到辅助队列中下一个树节点
}
}
cout<<endl<<"----------前序序列----------"<<endl;
preOrder(tree);
cout<<endl<<"----------非递归前序序列----------"<<endl;
preOrder_NonRecursion(tree);
cout<<endl<<"----------中序序列----------"<<endl;
inOrder(tree);
cout<<endl<<"----------非递归中序序列----------"<<endl;
inOrder_NonRecursion(tree);
cout<<endl<<"----------后序序列----------"<<endl;
postOrder(tree);
cout<<endl<<"----------非递归后序序列----------"<<endl;
postOrder_NonRecursion(tree);
cout<<endl<<"----------按层次遍历----------"<<endl;
levelOrder(tree);
return 0;
}
先比与普通二叉树,多了两个标记ltag和rtag
typedef struct ThreadNode
{
ElemType c;
struct ThreadNode *lchild;
struct ThreadNode *rchild;
int ltag,rtag; //记录指针指向的是谁,0代表指向孩子,1代表指某种序列的向前驱或后继
} ThreadNode,*ThreadTree;
#include
using namespace std;
typedef char ElemType;
typedef struct ThreadNode
{
ElemType c;
struct ThreadNode *lchild;
struct ThreadNode *rchild;
int ltag,rtag; //记录指针指向的是谁,0代表指向孩子,1代表指某种序列的向前驱或后继
} ThreadNode,*ThreadTree;
void BuildThreadTree(ThreadTree &T)
{
ThreadNode* array[5];
for(int i=0; i<5; i++)
{
array[i]=(ThreadNode*)malloc(sizeof(ThreadNode));
memset(array[i],0,sizeof(ThreadNode*));
array[i]->c='A'+i;
}
T=array[0];
array[0]->lchild=array[1];
array[0]->rchild=array[2];
array[1]->lchild=array[3];
array[2]->rchild=array[4];
}
void InThread(ThreadTree p,ThreadTree &pre) //pre记忆了当前节点的前一个节点,两层加引用,会改变全局变量;
{
if(p!=NULL)
{
InThread(p->lchild,pre); //递归找最边左孩子,也就是当前树中序遍历的第一个
if(p->lchild==NULL) //左指针为NULL
{
p->lchild=pre; //左指针指向前驱
p->ltag=1; //线索化标记置1
}
if(pre!=NULL&&pre->rchild==NULL) //pre节点右孩子为NULL
{
pre->rchild=p; //左指针指向后继
pre->rtag=1; //线索化标记置1
}
pre=p; //p成为当前节点
InThread(p->rchild,pre); //处理右子树
}
}
void CreatInThread(ThreadTree T)
{
ThreadNode* pre=NULL; //工作指针
if(T!=NULL)
{
InThread(T,pre);
pre->rchild=NULL;
pre->rtag=1;
}
}
ThreadNode* FirstNode(ThreadNode* p)
{
while(p->ltag==0)
p=p->lchild;
return p;
}
int main()
{
ThreadTree T; //树根
ThreadTree p; //接收用的变量
BuildThreadTree(T); //建立无线索的树
//CreatInThread(T); //加上线索
p=FirstNode(T);
cout<<"the first node is:"<<p->c<<endl;
return 0;
}