【二叉树遍历算法】——前/中/后序递归与非递归的实现

昨天面试了美团,面试官要求写出二叉树的中序遍历,要求实现递归与非递归方式。在写非递归的时候,一个while循环中忘记控制空指针,╮(╯▽╰)╭当时脑子一片空白不在状态。回来后告诉自己,这类问题以后绝不会发生!下述6种实现方法希望大家都能理解,并快速能手写代码出来。(理解,懂得思路才是王道,因为面试的时候人的情绪相对来说是比较紧张,需要掌握得更熟悉)

先(前)序递归遍历

void preorderTraverse(BiTree T){
	if(T){
		visit(T);
		preorderTraverse(T->lChild);
		preorderTraverse(T->rChild);
	}
}

中序递归遍历

void inOrderTraverse(BiTree T){
	if(T){
		inOrderTreverse(T->lChild);
		visit(T);
		inOrderTreverse(T->rChild);
	}
}

后序递归遍历

void lastorderTraverse(BiTree T){
	if(T){
		preorderTraverse(T->lChild);
		preorderTraverse(T->rChild);
		visit(T);
	}
}


先(前)序非递归遍历

void preorderTraverse(BiTree T){
	InitStack(S);
	Push(S,T);
	while(!StackEmpty(S)){
		while(GetTop(S,p) && p){
			//先打印再添加左子树
			visit(p);
			Push(S,p->lChild);
		}
		//弹出空指针
		Pop(S,p);
		//向右走一步
		if(!StackEmpty(S)){
			Pop(S,p);
			Push(S,p->rChild);
		}		
	}
}


中序非递归遍历

void inOrderTraverse(BiTree T){
	InitStack(S);
	Push(S,T);
	while(!StackEmpty(S)){
		//一直向左找,直到最后一个为空指针
		while(GetTop(S,p) && p)
			Push(S,p->lChild);
		//弹出空指针
		Pop(S,p);
		//向右走一步
		if(!StackEmpty(S)){
			Pop(S,p);
			visit(p);
			Push(S,p->rChild);
		}	
	}
}

后序非递归遍历

typedef struct{ 
	Node *p;      //二叉树结点
	int isVisited;  //1表示所指的有结点被访问过
}SNode

非递归后序
void lastorderTraverse(BiTree T){
	InitStack(S);	
	p = T;
	//一直往左边走
	while(p){ 
		Push(S,p,0);  p = p ->lchild;  //表示把当前根节点push进栈,同时isVisited值为0
	}
	while(!StackEmpty(S)){
		GetTop(S,sNode);
		
		//如果右子树已经访问过或者没有右子树
		if(!sNOde->p->rchild || sNode.isVisited == 1){
			Pop(S,p);
			visit(p);
		}else{
			//此时应该从右子树开始一直往左下方走到尽头
			sNode.isVisited = 1;
			p = sNode->rChild;
			while(p){
				Push(S,p,0);
				p = p->lChild;
			}			
		}		
	}
}

下面是我的理解和记住实现的几个技巧:

  • 递归实现中,先(前)/中/后序的visit函数的调用在两句递归调用语句的前/中/后位置。
  • 非递归实现中,先(前)和中序都是通过先把根压进栈,然后一直向左走走到尽头,弹出空指针后向右走一步,循环操作,不同在于:先(前)序遍历打印的时候是在添加左子树之前(因为根要先打印嘛),而中序是在添加右子树之前(因为根要比右子树先打印)。
  • 非递归实现中,打印的条件为当前没有右子树或者右子树已经访问过;需要借助一个结构体来记录右子树是否被访问过;同样需要先把根压栈再向左走到尽头;假设有一棵树(ABC),A为根,B为左子树,C为右子树,那么利用打印条件和压栈顺序,A其实不可能优先于BC打印的,而怎么控制B先于C打印呢?就是在每次不满足打印条件的时候,就拿到其右子树后一直往左下方走,把结点都push进栈就可以。



1.先序遍历非递归算法#define maxsize 100typedef struct{ Bitree Elem[maxsize]; int 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 }//PreOrderUnrec2.遍历非递归算法#define maxsize 100typedef struct{ Bitree Elem[maxsize]; int 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}//InOrderUnrec3.后序遍历非递归算法#define maxsize 100typedef enum{L,R} tagtype;typedef struct { Bitree ptr; tagtype tag;}stacknode;typedef struct{ stacknode Elem[maxsize]; int top;}SqStack;void PostOrderUnrec(Bitree t){ SqStack s; stacknode x; StackInit(s); p=t; do { while (p!=null) //遍历左子树 { x.ptr = p; x.tag = L; //标记为左子树 push(s,x); p=p->lchild; } while (!StackEmpty(s) && s.Elem[s.top].tag==R) { x = pop(s); p = x.ptr; visite(p->data); //tag为R,表示右子树访问完毕,故访问根结点 } if (!StackEmpty(s)) { s.Elem[s.top].tag =R; //遍历右子树 p=s.Elem[s.top].ptr->rchild; } }while (!StackEmpty(s));}//PostOrderUnrec
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值