数与二叉树(二)二叉树的遍历
一、二叉树的结构
typedef struct BTNode{
char data; //默认data域为char,可修改
struct BTNode *lchild;
struct BTNode *rchild;
}BTNode;
二、递归遍历
1.先序遍历
算法思想:
1)访问根节点
2) 访问左子树
3)访问右子树
代码实现:
void preorder(BTNode p){
if(p!=null){
visit(p);
preorder(p->lchild);
preorder(p->rchild);
}
}
2.中序遍历
算法思想:
1)访问左子树
2) 访问根节点
3)访问右子树
代码实现:
void inorder(BTNode p){
f(p!=null){
preorder(p->lchild);
visit(p);
preorder(p->rchild);
}
}
3.后序遍历
算法思想:
1)访问左子树
2) 访问右子树
3)访问根节点
代码实现:
void postorder(BTNode p){
if(p!=null){
preorder(p->lchild);
preorder(p->rchild);
visit(p);
}
}
三、非递归遍历
1、先序遍历
算法思想:
用堆栈储存结点,如果根节点不为空,根结点作为元素进栈。当栈不为空时进行循环,首先,出栈一个结点并访问,如果当前结点有右子树,则右子树进栈;如果有左子树,则左子树进栈。继续循环。(利用了栈的后进先出原则,因为要先访问左孩子所以右孩子先进栈)
代码实现:
void preorderNR(BTNode p){
if (p!=null){
stack=(*BTNode)malloc(sizeof(BTNode));//定义一个栈
int top = -1;//初始化栈
stack[++top]=p;
while(top != -1){
p=stack[top--];//下一个结点出栈
visit(p);
if(p->rchild!=null)
stack[++top]=p->rchild;//若右子树存在,则进栈
if(p->lchild!=null)
stack[++top]=p->lchild;//若左子树存在,则进栈
}
}
}
2、中序遍历
算法思想:
用堆栈储存结点,当当前结点不为空或者栈不为空时,进入循环,如果当前结点不为空则当前结点进栈,且当前结点指向当前结点的左孩子,直到当前结点为空,如此栈顶元素便是最左边的结点。当前结点出栈,访问之,当前结点的指针指向其右孩子,继续循环。
代码实现:
void inorderNR(BTNode p){
stack=(*BTNode)malloc(sizeof(BTNode));//定义一个栈
int top = -1;//初始化栈
while (p!=null || top!=-1){
while(p!=null){
stack[++top]=p;
}//寻找最左边的结点
p=stack[top--];
visit(p);
p=p->rchild;//右子树进栈
}
}
此算法无需判断右结点是否为空,因为如果右结点为空,栈顶结点便会直接出栈。
3、后序遍历
(1)算法一
算法思想:
后序遍历的非递归算法实现比较复杂。因为我们找到遍历左子树时,根节点及其左子树的根节点要进栈,当遍历完左子树,我们要让其根节点出栈,找他的右子树,这个过程根节点及其右子树的根节点又要进栈,遍历完右子树,根节点出栈并访问。由于每个非终端结点都经历了两次出栈,而第二次出栈我们才可以访问,所以我们引入一个同步栈,第一次进栈时,同步进入元素0,第二次进栈时,同步进入元素1,出栈时,如果同步栈出栈元素为1就访问。
代码实现:
void postorderNR(BTNode p){
BTNode *stack1[maxsize]; //定义一个栈用于储存结点
int *stack2[maxsize]; //定义一个同步栈用于判断是否访问输出结点
int top = -1;//初始化两个栈
int tag;//用于同步栈输出
while(p!=null || top!=-1){
while(p!=null){ stack1[++top]=p;
stack2[++top]=0;
p=p->lchild;} //寻找最左边的结点
p=stack1[top];
tag=stack2[top--];
if(tag==0){
while(p!=null){ stack1[++top]=p;
stack2[++top]=1;
p=p->rchild;}
}//寻找该子树下最右边的结点
else if(tag==1)
{visit(p);
p=null;//避免死循环
}
}
}
(2)算法二
算法思想:
比较二叉树的先序遍历序列和后序遍历序列,不难发现,后序遍历的逆序列,就是先序遍历改变访问左右子树的顺序得到的序列。即后序遍历的逆序列就是先访问结点,再访问右子树,再访问左子树得到的序列。那么我们将如此访问得到的序列压入栈中后,输出栈中的全部元素,即可得到后序遍历的序列。
算法实现:
postorderNR(BTNode p){
if(p !=null ){
BTNode *stack1[maxsize]; //定义一个栈用于储存结点
BTNode *stack2[maxsize] //定义一个同步栈用于判断是否访问输出结点
int top1 = -1;//初始化栈1
int top2 = -1;//初始化栈2
stack1[++top1]=p;
while(top1!=-1){
p = stack[top1--];
stack[++top2]=p;
if(p->lchild != null)
stack1[++top1]=p->lchild;
if(p->rchild != null)
stack1[++top1]=p->rchild;
}
while(top2 != -1){
p=stack[top2--];
visit(p);
}
}
}
四、层次遍历
算法思想:层次遍历要求自上而下、自左而右访问二叉树。除根结点外,每一层的结点都是上一层结点的孩子结点。利用队列先进先出的性质,每个结点出队时,他的子结点就进入队列,队列为空则循环结束。
代码实现:
void level(BTNode p){
if(p!=null){
que=(*BTNode)malloc(sizeof(BTNode));
int front, rear;
front=0;
rear=front;
que(++rear)=p;
while(rear>front){
p=que(front--);
visit(p);
if(p->lchild != null)
que[++rear]=p.lchild;
if(p->rchild != null)
que[++rear]=p.rchild;
}
}
}
浙公网安备 33010602011771号