非递归遍历二叉树实现

注意事项:

  1. 先序非递归算法的思想:1.二叉树指针不为空时,根指针入栈,指针指向左孩子,并将根结点输出。如果二叉树指针为空,根指针出栈,指针指向右孩子。(每一次出栈后指针指向其右孩子,可以保证每一个左右结点都可以遍历到)
  2. 后序遍历非递归算法:从根结点开始,将所有最左结点全部压栈,每当一个结点出栈时,都先扫描该结点的右子树,只有当一个结点的左孩子和右孩子结点均被访问过了,才能访问结点自身。
  3. 后序遍历算法的一个特性:就是当访问某个结点时,栈中所保存的元素,正好是这个结点的所有祖先。根据这个特性,就很容易解决下面如下问题:
    (1).当给定一个叶子结点,要求输出该叶子结点的所有祖先
    (2).输出根结点到所有叶子结点的路径
    (3).如果二叉树结点的值是数值,那么求每条路径上值之和,也可以利用二叉树后序遍历的非递归算法这个特性
  4. 半小时BUG:在定义链栈头指针的时候,没有将其初始化指向一处分配好的空间,导致了野指针。
#include<stdio.h>
#include<stdlib.h>
#define StackElement BiTree
typedef struct BiTNode
{
	char data;
	struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
 
typedef struct LSNode
{
	StackElement data;
	struct LSNode * next;
}LSNode, *LinkStack;
 
void InitStack(LinkStack *s);
void Push(LinkStack s, StackElement i);
void Pop(LinkStack s, StackElement *i);
int StackEmpty(LinkStack s);
 
void CreatBiTree(BiTree *t);//这里由于要递归输入值,所以不能用返回指针值的方法改变结构指针,只能用指向指针的指针来做形参!
void DestroyBiTree(BiTree *t);
void PreOrderTraverse(BiTree t);
void InOrderTraverse(BiTree t);
void PostOrderTraverse(BiTree t);
 
int main()
{
	BiTree t = NULL;
	CreatBiTree(&t);
	printf("Non-recursive traversal is\n");
	PreOrderTraverse(t);
	InOrderTraverse(t);
	PostOrderTraverse(t);
	return 0;
}
 
void InitStack(LinkStack *s)
{
	(*s)->next = NULL;
}
void Push(LinkStack s, StackElement i)
{
	LinkStack p = (LinkStack)malloc(sizeof(LSNode));
	p->data = i;
	p->next = s->next;
	s->next = p;
}
void Pop(LinkStack s, StackElement *i)
{
	LinkStack p = s->next;
	s->next = p->next;
	(*i) = p->data;
	free(p);
}
int StackEmpty(LinkStack s)
{
	if (s->next)
		return 0;
	else
		return 1;
}
 
 
void CreatBiTree(BiTree *t)//先序创建
{
	char ch;
	scanf_s("%c", &ch);
	if (ch == ' ')//出口
	    *t = NULL;
	else
	{
		*t = (BiTree)malloc(sizeof(BiTNode));
		if (!*t)
			exit(1);
		(*t)->data = ch;
		CreatBiTree(&((*t)->lchild));
		CreatBiTree(&((*t)->rchild));
	}
 
}
void DestroyBiTree(BiTree *t)
{
	if (*t)
	{
		if ((*t)->lchild)
			DestroyBiTree(&((*t)->lchild));
		if ((*t)->rchild)
			DestroyBiTree(&((*t)->rchild));
		free(*t);
		(*t) = NULL;
	}
}
 
void PreOrderTraverse(BiTree t)
{
	LinkStack s = (LinkStack)malloc(sizeof(LSNode));
	InitStack(&s);
	while (t || !StackEmpty(s)) //只是改变T的指向,没有改变二叉树的顺序。
	{
		if (t)//先序遍历是根左右
		{
			Push(s, t);
			printf("%c->", t->data);
			t = t->lchild;
		}
		else
		{
			Pop(s, &t);
			t = t->rchild;
		}
	}
	printf("NULL\n");
}
void InOrderTraverse(BiTree t)
{
	LinkStack s = (LinkStack)malloc(sizeof(LSNode));
	InitStack(&s);
	while (t || !StackEmpty(s))
	{
		if (t)
		{
			Push(s, t);
			t = t->lchild;
		}
		else
		{
			Pop(s, &t);
			printf("%c->", t->data);
			t = t->rchild;
		}
	}
	printf("NULL\n");
}
/*后序遍历算法1:用两个栈,第一个用根右左遍历但不输出,另一个栈在第一个栈遍历时记录所有进栈的结点,最后输出第二个栈的结点。*/
while(T || !empty_Stack(S)){
    if(T){
      Push_Stack(S,T);
      Push_Stack(CS,T);
      T=T->right;
    }else{
      T=Pop_Stack(S)->data;
      T=T->left;
    }
  }
  while(CS->top!=NULL){
    printf("%c",CS->top->data->element);
    CS->top=CS->top->next;
  }

/*算法2:在结构中加入flag记录二次进栈的结点属性。用先序遍历结点,当指针为空时,立即出栈,并在出栈后将此结点flag设为1(表示左结点已经访问过),再将出栈结点入栈,指针指向右孩子。*/
while (T || !empty_Stack(S)) {
    if (T) {
      T->flag = 0;
      Push_Stack(S, T);
      T = T->left;
    }
    else {
      T = Pop_Stack(S)->data;
      if (T->flag == 0) {
        T->flag = 1;
        Push_Stack(S, T);
        T = T->right;
      }
      else {
        if (T->flag == 1) {
          printf("%c", T->element);
          T = NULL;
        }
      }
    }

/*以下代码思路来自 <https://www.jb51.net/article/126425.htm> 
  侵删
  
算法3:每次找到最左边的结点,若此结点右孩子也是空,则输出,否则指针指向右孩子。*/
void PostOrderTraverse(BiTree t) //这个顺序栈结构做的非常不起眼,而且简单。
{
	BiTree SqStack[100];
	int top = -1;
	int flag = 1;
	BiTree p;
	while (t != NULL || top > -1) 
	{
		while (t != NULL)           // 循环,将所有最左结点压栈
		{
		top++;
		SqStack[top] = t;
		t = t->lchild;
		}
		flag = 1;                          // 辅助变量flag为1表示当前结点的左孩子为空或者已被访问
		p = NULL;                          // 指针变量p指向当前结点的前驱结点
		while (top > -1 && flag == 1)      // 不断判断条件退栈输出或者有右孩子的重新开始大循环
		{
			t = SqStack[top];             // 注意:这里只是获取栈顶元素,而并没有出栈
			if (t->rchild == p || t->rchild == NULL)// 如果当前结点右孩子为空,或者是上次访问过的,则访问当前结点
			{
				top--;                     // 当前结点出栈
				printf("%c->", t->data);
				p = t;                     // 指针变量指向当前结点
			}
			else                           // 如果当前结点右孩子不为空,则先去处理右孩子
			{
				t = t->rchild;             // 处理右孩子
				flag = 0;                  // *t的左孩子未被访问,flag置为0
			}
		}
	}
		printf("NULL\n");
	
}

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值