一、预备知识(栈和队列)
这两个作为最基础的数据结构相信大家已经掌握了,但是如果要对二叉树进行操作的话,还是有必要回顾一下队列(先进后出)和栈(先进先出)的基本性质和操作的。
队列的基础代码
typedef struct LinkNode {
BiTree data;
struct LinkNode* next;
}LinkNode;
typedef struct {
LinkNode* front, * rear;//头指针,尾指针不用现设
}LinkQueue;
void InitQueue(LinkQueue& Q)
{
Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next = NULL;
}
bool IsEmpty(LinkQueue Q)
{
if (Q.front == Q.rear)
return true;
else return false;
}
void EnQueue(LinkQueue &Q,BiTree x)
{
LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
s->data = x;
s->next = NULL;
Q.rear->next = s;
Q.rear = s;
}
bool DeQueue(LinkQueue& Q, BiTree& x)
{
if(Q.front == Q.rear) return false;
LinkNode* p = Q.front->next;
x = p->data;
Q.front->next = p->next;//删除第二个节点,也就是队头
if (Q.rear == p)
Q.rear = Q.front;
free(p);
return true;
}
栈的基础代码
typedef struct {
BiTree data[MaxSize];
int top;
}SqStack;
void InitStack(SqStack &S) {
S.top = -1;
}
bool StackEmpty(SqStack& S) {
return (S.top == -1) ? true : false;
}
bool Push(SqStack& S, BiTree x)
{
if (S.top == MaxSize - 1) return false;
S.data[++S.top] = x;
return true;
}
bool Pop(SqStack& S, BiTree& x)
{
if (S.top == -1) return false;
x = S.data[S.top--];
return true;
}
bool GetTop(SqStack& S, BiTree& x)
{
if (S.top == -1) return false;
x = S.data[S.top]; return true;
}
二、二叉树的建立
二叉树的建立可以有很多种方法,下面是用链表建立的方法。就是把数据先存入节点塞进链表里面,再逐层取出节点给他们连成二叉树的样子。
typedef struct BiTNode{
char c;
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode,*BiTree;
typedef struct tag{
BiTree p;//树的某一个结点的地址值
struct tag *pnext;
}tag_t,*ptag_t;
int main()
{
BiTree pnew;
int i,j,pos;
char c;
BiTree tree=NULL;//树根
ptag_t phead=NULL,ptail=NULL,listpnew,pcur;//phead就是队列头,ptail就是队列尾
while(scanf("%c",&c)!=EOF)
{
if(c=='\n')
{
break;
}
pnew=(BiTree)calloc(1,sizeof(BiTNode));
pnew->c=c;//数据放进去
listpnew=(ptag_t)calloc(1,sizeof(tag_t));//给队列结点申请空间
listpnew->p=pnew;
if(NULL==tree)
{
tree=pnew;//树的根
phead=listpnew;//队列头
ptail=listpnew;//队列尾
pcur=listpnew;
continue;
}else{
ptail->pnext=listpnew;//新结点放入链表,通过尾插法
ptail=listpnew;//ptail指向队列尾部
}//pcur始终指向要插入的结点的位置
if(NULL==pcur->p->lchild)//如何把新结点放入树
{
pcur->p->lchild=pnew;//把新结点放到要插入结点的左边
}else if(NULL==pcur->p->rchild)
{
pcur->p->rchild=pnew;//把新结点放到要插入结点的右边
pcur=pcur->pnext;//左右都放了结点后,pcur指向队列的下一个
}
}
printf("--------前序遍历----------\n");
preOrder(tree);
printf("\n--------中序遍历------------\n");
InOrder(tree);
printf("\n--------后序遍历------------\n");
PostOrder(tree);
printf("\n--------前序非递归遍历----------\n");
PreOrder2(tree);
printf("\n--------中序遍历非递归------\n");
InOrder2(tree);
printf("\n--------层次遍历-----------\n");
LevelOrder(tree);
printf("\n");
system("pause");
}
三、前中后序遍历
前序遍历,首先访问父节点,然后是左孩子和右孩子。非递归方法就是从左到右,从上到下溜达,把路过的节点访问完存到栈里面,然后在没有子节点的时候就出栈。
中序遍历的原理和上面差不多,唯一的区别是他是出栈后访问,就不再赘述了。
void preOrder(BiTree p)
{
if(p!=NULL)
{
putchar(p->c);//等价于visit函数
preOrder(p->lchild);
preOrder(p->rchild);
}
}
//中序遍历 hdibjeafcg
void InOrder(BiTree p)
{
if(p!=NULL)
{
InOrder(p->lchild);
putchar(p->c);
InOrder(p->rchild);
}
}
//hidjebfgca
void PostOrder(BiTree p)
{
if(p!=NULL)
{
PostOrder(p->lchild);
PostOrder(p->rchild);
putchar(p->c);
}
}
//中序遍历非递归,非递归执行效率更高,考的概率很低
void InOrder2(BiTree T)
{
SqStack S;
InitStack(S);BiTree p=T;
while(p||!StackEmpty(S))//逻辑或||
{
if(p)
{
Push(S,p);
p=p->lchild;
}else{
Pop(S,p);putchar(p->c);
p=p->rchild;
}
}
}
void PreOrder2(BiTree T)
{
SqStack S;
InitStack(S);BiTree p = T;
while (p || !StackEmpty(S)) {
if (p)
{
putchar(p->c);
Push(S, p);
p = p->lchild;
}
else {
Pop(S, p);
p = p->rchild;
}
}
}
四、层序遍历
根节点入队。然后出队并且检查他们是否有孩子,如果有就按照左孩子右孩子的顺序,依次入队。 然后循环往复,直到队列里面一个节点都没有。
就拿上图来说,队列里的情况为:A-->(A)BC-->(A)(B)(C)DEFG-->(A)(B)(C)(DE)(FG)。其中括号内是被删除的节点。A的出队的同时BC入队,然后你就会发现每一次访问到下一层节点时队列里面就正好是这个层节点的顺序。
void LevelOrder(BiTree T)
{
LinkQueue Q;
InitQueue(Q);
BiTree p;
EnQueue(Q,T);//树根入队
while(!IsEmpty(Q))
{
DeQueue(Q,p);
putchar(p->c);
if(p->lchild!=NULL)
EnQueue(Q,p->lchild);
if(p->rchild!=NULL)
EnQueue(Q,p->rchild);
}
}