二叉树遍历的非递归实现

前、中序的基本结构,表现为先序压栈(NLR),中序出栈(LNR)
压栈的前半部分if(p),三种遍历都一样,因为都是L开始,所以将所以左子压入,直到最左的结点。

while(p||!isEmpty(S)){
	if(p){    
		//此处cout<<p->data;即先序         
		push(S,p);
		p=p->lchild;     // Order(T->lchild);
	}
	else{
		pop(S,p);       
		//此处cout<<p->data;即中序
		p=p->rchild;   //Order(T->rchild)
	}
}

入栈顺序为(N)LR,故前序在压栈push前输出
出栈顺序为L(N)R,故中序在出栈pop后输出
(R是用来转道的)

1.前序:

void PreOrder2(BiTree T) {
	InitStack(S);
	BiNode* p = T;
	while (p || !isEmpty(S)) { //当根的左子树及根全pop是,栈为空,p指向根右子树
		if (p) {
			cout << p->data;        //(N)
			push(S, p);
			p = p->lchild;        //(L)
		}
		else {
			pop(S, p);
			p = p->rchild;     //(R)
		}
	}
}

对于前序的非递归,先压根(N),再压左子树(L),左子树出栈,根出栈,再压右子树(R),右子树出栈,结束。
前序的循环条件while(p||!isEmpty(S))
(1)一开始S是空的
(2)在根的左子树全部出栈,根也出栈后,栈为空,再压右子树,在右子树压栈出栈过程中可能再次出现栈空的状态。所以加个p,防止因为栈空断了循环。

2.中序:

void InOrder2(BiTree T) {
	InitStack(S);
	BiNode* p = T;
	while (p || !isEmpty(S)) {   //同先序,当根及其左子树全pop时,栈为空
		if (p) {
			push(S, p);
			p = p->lchild;        //(L)
		}
		else {
			pop(S, p);	cout << p->data;   //(N)
			p = p->rchild;    //转右换道(R)
		}
	}
}

对于中序的非递归:压根,压左子树,左子树出栈(L),根出栈(N),压右子树,右子树出栈(R),结束。
中序的循环条件while(p||!isEmpty(S)),原因同前序。

3.后序:

void PostOrder2(BiTree T) {
	InitStack(S);
	BiNode* p = T;
	BiNode* r = NULL;  		 //记录上一个出栈的结点,防止右边重复遍历
	while (p || !isEmpty(S)) {
		if (p) {
			push(S, p);
			p = p->lchild;    //只要p不为空左子全部入栈(L)
		}
		else {
			p = GetTop(S);
			if (p->rchild && p->rchild != r)   //右子为空且不是上一个出栈结点
				p = p->rchild;           	   //(R)
			else {
				pop(S, p);
				cout << p->data;  //(N)
				r = p;       //重置r为出栈结点
				p = NULL;    //置空!!!防止重复遍历左边
			}
		}
	}
}

对于中序的非递归:压根,压左子树,左子树出栈(L),压右子树,右子树出栈(R),根出栈(N),结束。
压栈顺序为先序NLR。
出栈顺序为后序LRN。
中序的循环条件while(p||!isEmpty(S)):因为一开始S是空,后序的S之后最后根输出才会空,所以中途不会出现栈空的状态。

每到右结点是我们都要取出栈顶结点(GetTop(S))来辅助判断。
所有我们需要r保存上一个出栈的结点,是否转向右子树,判断其不为NULL且不是已经访问过的才能p=p->rchild
并且在没有右结点而去回溯的时候,将p=NULL置空,防止返回后又向下访问左子树。

综述

三个非递归的压栈顺序相同,都是NLR,因为这个:

if(p){
	push(S,p);
	p = p->lchild;
}

只有p存在,总是一路压左。


先序中序出栈相同:
总是左,根先出栈,在去转向右子树压栈。LNR。

else{
	pop(S,p);
	p=p->rchild;
}

后序特殊,因为要确定右子未被访问,防止重复入右。

else {
	pop(S, p);
	cout << p->data;  //(N)
	r = p;       //重置r为出栈结点
	p = NULL;    //置空!!!防止重复遍历左边
}

以下是转右的判断,不是直接p=p->rchild; 然后 pop(S,p);
如果没处理,否则每次都会将右边都遍历一遍输出。。。

p = GetTop(S);
if (p->rchild && p->rchild != r)   //右子为空且不是上一个出栈结点
	p = p->rchild;           	   //(R)

用if…else,也是因为转右和输出是不同的两部分,实际上L、N、R三部分都是if…else语句区分开。
对于pop(S,p);之后也要进行处理,重置r=p,防止重复访问右子树,p=NULL,防止重复访问左子树。

else{
		pop(S,p);
		cout<<p->data;
		r=p;
		p=NULL;
	}



自己写的玩的

1.前序

非递归实现需要借助栈——先压根,栈顶出栈,先右后左压栈
递归实现是NLR,输出当前结点,再进入左子树遍历,然后进入右子树遍历。每次<输出根,向左>*n,其中遇到右子树转右,在进行前面的操作。

非递归实现:
1.根压栈
2.循环进行——输出栈顶,若有右压右,有左压左

和层次遍历及其的像!!
层次遍历:
1.根进队
2.循环进行——输出队头,有左进左,有右进右
不同点:1.先序是栈,层次是队列。 2.先序是先右后左,层次是先左后右。

void PreOrder1(BiTree T) {
	InitStack(S);
	push(S,T);  //根入栈
	BiNode* p;  //工作指针
	while (!isEmpty(S)) {
		pop(S, p);
		cout << p->data;
		if (p->rchild != NULL)
			push(S, p->rchild);  //右子进栈
		if (p->lchild != NULL)
			push(S, p->lchild);  //左子进栈
	}
}

2.中序

非递归实现需要借助栈,因为中序是先遍历左子树,然后再访问根,然后遍历右子树。
所以我们需要先压所有左子,然后出栈,看是否有右子,有右子就要进入右子树,继续前面的操作。

void InOrder1(BiTree T) {
	InitStack(S);
	BiNode* p;    
	while (T || !isEmpty(S)) {
		while (T != NULL) {     //(L)
			push(S, T);
			T = T->lchild;   //将所以左子压入
		}
		pop(S, p);
		cout << p->data;  	//取栈顶(N)
		if (p->rchild != NULL)     //遇到右就换道(R)
			T = p->rchild;
	}
}

辅助的栈

想折叠,但是CSDN的markdown没有找到折叠的方法,github上面的markdown有折叠的方法。

//顺序栈
typedef struct SqStack {
	BiNode* val[Maxsize];
	int top;   //top指向栈顶元素
}SqStack;
void InitStack(SqStack& S) {
	S.top = -1;
} 
bool isEmpty(SqStack S) {
	return S.top == -1 ? 1 : 0;
}
bool push(SqStack& S, BiNode* x) {
	if (S.top == Maxsize - 1)
		return 0;
	S.val[++S.top] = x;
	return 1;
}
bool pop(SqStack& S, BiNode*& x) {
	if (S.top == -1)
		return 0;
	x = S.val[S.top--];
	return 1;
}
BiNode* GetTop(SqStack S) {
	return S.top == -1 ? NULL : S.val[S.top];
}
链栈?狗都不用!
打了几天链栈,手都打累了,才发现顺序栈可以....
//链栈
typedef struct Linode{
	BiNode* node;
	Linode* next;   // 前面链队是LiNode
}Linode,*LinkStack;
void InitStack(LinkStack& S) {  //带头结点
	S = new Linode;
	S->next = NULL;
}
bool isEmpty(LinkStack S) {
	return S->next != NULL ? 0 : 1;
}
BiNode* GetTop(LinkStack S) {
	return S->next == NULL ? NULL : S->next->node;
}
void push(LinkStack S, BiNode* x) {
	Linode* p = new Linode;
	p->node = x;
	p->next = S->next;
	S->next = p;
}
bool pop(LinkStack S, BiNode* &x) {
	if (S->next == NULL)  //判栈空
		return false;
	x = S->next->node;
	Linode* p = S->next;
	S->next = p->next;
	delete(p);
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值