【二叉树】构建,递归与非递归的前/中/后序遍历

参考https://www.cnblogs.com/polly333/p/4740355.html#

好像没在网上看到有释放二叉树结点的函数?但是毕竟用到了malloc,懒得写了,想了一下思路大概是借用递归的前序遍历函数,将printf语句改为push语句将结点压入栈。最后将栈里面的结点依次释放就可以了。或者不用入栈,直接用后续遍历递归的方式释放(把printf改为释放),比前面的更简单一点(如果有更简便的思路也请告诉我?)

还有一个要改进的地方,大多数跟栈有关的函数返回值都是int型的,但在实际使用中并没有判断是否进/出/取栈成功(是我懒了)。

然后有关二叉树的层次遍历,求二叉树深度之后在研究吧。。。

靠非递归的遍历真的很烦。很烦。很烦。老师ppt里的方法还没仔细看但是感觉思路不清晰也有bug,所以全都用的链接中的方法

 

 输入用这个例子:

ABE ( ) L ( )( )( ) DHM ( )( ) I ( )( ) J ( )( )

一对括号代表一个空格,左右子树为空都要用空格代表NULL表示出来,不然CreateTree函数哪知道你哪里是空的

//#define _CRT_SECURE_NO_DEPRECATE      //vs下要用

#include <stdio.h>
#include <stdlib.h>

#define SIZE 20

enum ReturnVal { CREATE_NO, CREATE_OK, EMPTY_NO, EMPTY_OK, FULL_NO, FULL_OK, PUSH_NO, PUSH_OK, POP_NO, POP_OK, GET_NO, GET_OK};

typedef struct BiTree
{
	char data;

	struct BiTree *lchild, *rchild;

} BiTree;

typedef struct Stack
{
	BiTree *stack_array[SIZE];

	int top;

} Stack;

int IsEmptyStack(Stack *s)
{
	if (s->top == -1)
	{
		return EMPTY_OK;
	}
	return EMPTY_NO;
}

int IsFullStack(Stack *s)
{
	if (s->top == SIZE - 1)
	{
		return FULL_OK;
	}
	return FULL_NO;
}

void InitStack(Stack *s)
{
	s->top = -1;
}

int PushStack(Stack *s, BiTree *t)
{
	if (IsFullStack(s) == FULL_OK)
	{
		return PUSH_NO;
	}
	else
	{
		s->top++;
		s->stack_array[s->top] = t;
		return PUSH_OK;
	}
}

int PopStack(Stack *s, BiTree **t)
{
	if (IsEmptyStack(s) == EMPTY_OK)
	{
		return POP_NO;
	}
	else
	{
		*t = s->stack_array[s->top];
		s->top--;
		return POP_OK;
	}
}

int GetTopStack(Stack *s, BiTree **t)
{
	if (IsEmptyStack(s) == EMPTY_OK)
	{
		return GET_NO;
	}
	else
	{
		*t = s->stack_array[s->top];		//时刻注意stack_array这个数组里放的是指向BiTree的指针,而不是BiTree的data或Bitree本身
		return GET_OK;
	}
}

int CreateNode(BiTree **t)
{
	int count = 10;

	do
	{
		*t = (BiTree*)malloc(sizeof(BiTree));
		count--;

		if (count == 0)
		{
			return CREATE_NO;
		}

	} while (*t == NULL);

	return CREATE_OK;
}

void CreateTree(BiTree **t)
{
	char ch;
	scanf("%c", &ch);

	if (ch == ' ')
	{
		*t = NULL;
	}
	else
	{
		if (CreateNode(t) == CREATE_NO)
		{
			printf("malloc error!\n");
			exit(-1);
		}
		else
		{
			(*t)->data = ch;
			CreateTree(&((*t)->lchild));
			CreateTree(&((*t)->rchild));
		}
	}
}

void PreOrderTraverse(BiTree *t)        //递归的前序遍历
{
	if (t != NULL)
	{
		printf("%c", t->data);
		PreOrderTraverse(t->lchild);
		PreOrderTraverse(t->rchild);
	}
}

void InOrderTraverse(BiTree *t)         //递归的中序遍历
{
	if (t != NULL)
	{
		InOrderTraverse(t->lchild);
		printf("%c", t->data);
		InOrderTraverse(t->rchild);
	}
}

void PostOrderTraverse(BiTree *t)       //递归的后序遍历
{
	if (t != NULL)
	{
		PostOrderTraverse(t->lchild);
		PostOrderTraverse(t->rchild);
		printf("%c", t->data);
	}
}

//对于任一结点p:
//a.访问结点p,并将结点p入栈;
//b.判断结点p的左孩子是否为空,若为空,
//则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点p,
//循环置a;若不为空,则将p的左孩子置为当前结点p;
//c.直到p为空,并且栈为空,则遍历结束。


void NoPreOrderTraverse(BiTree *t)      //非递归的前序遍历
{
	Stack s;
	InitStack(&s);

	if (t == NULL)
	{
		printf("the tree is empty!\n");
		return;
	}

	while (t != NULL || IsEmptyStack(&s) == EMPTY_NO)
	{
		while (t != NULL)
		{
			PushStack(&s, t);
			printf("%c", t->data);
			t = t->lchild;
		}
		if (IsEmptyStack(&s) == EMPTY_NO)
		{
			PopStack(&s, &t);            //要传指针t的地址!不能直接传指针t,这里要改变t的指向
			t = t->rchild;              //要二级指针
		}

	}

}

//根据中序遍历的顺序,对于任一结点,优先访问其左孩子,
//而左孩子结点又可以看做一个根结点,然后继续访问其左孩子结点,
//直到遇到左孩子结点为空的结点才停止访问,然后按相同的规则访问其右子树。
//其处理过程如下:

//对于任一结点:
//a.若其左孩子不为空,则将p入栈,并将p的左孩子设置为当前的p,
//然后对当前结点再进行相同的操作;
//b.若其左孩子为空,则取栈顶元素并进行出栈操作,
//访问该栈顶结点,然后将当前的p置为栈顶结点的右孩子;
//c.直到p为空并且栈为空,则遍历结束。

void NoInOrderTraverse(BiTree *t)		//非递归的中序遍历
{
	Stack s;
	InitStack(&s);

	if (t == NULL)
	{
		printf("the tree is empty!\n");
		return;
	}

	while (t != NULL || IsEmptyStack(&s) == EMPTY_NO)
	{
		while (t != NULL)
		{
			PushStack(&s, t);
			t = t->lchild;
		}

		if (IsEmptyStack(&s) == EMPTY_NO)
		{
			PopStack(&s, &t);
			printf("%c", t->data);
			t = t->rchild;
		}
	}
}

//后序遍历的非递归实现是三种遍历方式中最难的一种。
//因为在后序遍历中,要保证左孩子和右孩子都已被访问,
//并且左孩子在右孩子之前访问才能访问根结点,
//这就为流程控制带来了难题。下面介绍一种思路。

//要保证根结点在左孩子和右孩子访问之后才能访问,
//因此对于任一结点p,先将其入栈。
//若p不存在左孩子和右孩子,则可以直接访问它,
//或者p存在左孩子或右孩子,但是其左孩子和右孩子都已经被访问过了,
//则同样可以直接访问该结点。
//若非上述两种情况,则将p的右孩子和左孩子依次入栈,
//这样就保证了每次取栈顶元素的时候,左孩子在右孩子之前别访问,
//左孩子和右孩子都在根结点前面被访问。


void NoPostOrderTraverse(BiTree *t)			//非递归的后续遍历
{
	Stack s;
	InitStack(&s);

	BiTree* cur;			//当前结点
	BiTree* pre = NULL;	//前一次访问的结点

	if (t == NULL)
	{
		printf("the tree is empty!\n");
		return;
	}

	PushStack(&s, t);
	while (IsEmptyStack(&s) == EMPTY_NO)
	{
		GetTopStack(&s, &cur);
		if ((cur->lchild == NULL && cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
		{
			printf("%c", cur->data);
			PopStack(&s, &t);
			pre = cur;
		}
		else
		{
			if (cur->rchild != NULL)
			{
				PushStack(&s, cur->rchild);
			}
			if (cur->lchild != NULL)
			{
				PushStack(&s, cur->lchild);
			}
		}
	}


}

int main()
{
	BiTree *t;

	CreateTree(&t);

	printf("递归的前序遍历:");
	PreOrderTraverse(t);
	printf("\n");

	printf("递归的中序遍历:");
	InOrderTraverse(t);
	printf("\n");

	printf("递归的后序遍历:");
	PostOrderTraverse(t);
	printf("\n");

	printf("非递归的前序遍历:");
	NoPreOrderTraverse(t);
	printf("\n");

	printf("非递归的中序遍历:");
	NoInOrderTraverse(t);
	printf("\n");

	printf("非递归的后序遍历:");
	NoPostOrderTraverse(t);
	printf("\n");

	//system("pause");        vs下要用
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值