二叉树的非递归遍历算法

#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node {
	datatype data;
	struct node *left, *right;
}BitTree;

//添加辅助栈的结构:主要是用来保存树的节点
//简言之,这个就是用来存树节点的栈
typedef BitTree BitTreeNode;
typedef int BOOL;
#define TRUE 1
#define FALSE 0


typedef struct linknode {
	BitTreeNode data;
	struct linknode* pNext;
}LinkStack;

LinkStack* InitLkStack(void) {
	LinkStack* p = NULL;
	p = (LinkStack*)malloc(sizeof(LinkStack));
	if (p != NULL)
		p->pNext = NULL;
	return p;
}//建立一个带头节点的链表作为链栈

BOOL IsEmptyLkMazeStack(LinkStack* top) {
	return (top->pNext == NULL);
}

LinkStack* PushLkStack(LinkStack* top, BitTreeNode* elem) {
	LinkStack* p=NULL;
	//深拷贝的bug修订,如果传入的是空指针,那么不能访问,修订人丁宋涛
	if (elem != NULL)
	{
		p = (LinkStack*)malloc(sizeof(LinkStack));
		//todo:我们用新建节点,对传入的二叉树节点做一个深拷贝
		p->data.data = elem->data;
		p->data.left = elem->left;
		p->data.right = elem->right;
		p->pNext = top; //p节点尾插入lkStack
		top = p;//top栈顶指针上移
	}
	
	

	//我们思考,既然我们允许空指针进入到我们的栈结构,那么我们如何修订
	//我们的出栈?
	//top为空以为着?
	//意味着是否和栈空条件冲突了?
	//如果我们让我们的NULL进入栈空间,那么出栈也会造成空指针
	//这意味着当前的节点不动

	return top;
}

//出栈:pData是传入的一个“临时”空间用来接收出栈的节点信息。
LinkStack* PopLkStack(LinkStack* top, BitTreeNode *pData) {
	LinkStack* p;
	if (top != NULL) {
		//*pData = top->data;
		pData->data = top->data.data;
		pData->left = top->data.left;
		pData->right = top->data.right;
		//pData实际上深拷贝了一份节点信息,所以释放栈中节点,不会影响
		//节点数据
		p = top;
		top = p->pNext;
		free(p);
	}
	return top;
}



//辅助队列Q,这是用来存关系的
BitTree* Q[16];//这是一个指针数组,它将缓存节点的地址,因为这个地址将以
			   //left域,或者right域进入二叉链表,它本身不维护i,2i,2i+1的关系
			   //他的关系通过front,rear来维护

			   //按照直观的建立,我们首先想到的是层次法
			   //我们根据层次,来逐一将二叉树建立
			   //输入的数组是按照完全二叉树的编号规则设立,即
			   //数组角标反映了节点位置关系(存联系)
			   //逐个扫描数组,直到所有的节点都存取完毕之后,就结束
			   //约定,0表示数组当前元素为空,最后一个节点标志是-999
BitTree* CreateBinTree(int arr[]) {
	int i = 1;
	//只要我们没有扫描完元素,那么这个二叉链表就没有完成
	//只要扫描,我们就malloc一个节点,然后把这个节点存入left域或者right域
	int front = 1, rear = 0;
	BitTree* root = NULL;
	BitTree* s;//暂存节点
	while (arr[i] != -999) {
		s = NULL;
		if (arr[i] != 0) {//意味着这个不是空节点,那么我们就要分配空间
			s = (BitTree*)malloc(sizeof(BitTree));
			s->data = arr[i];//存数值
			s->left = NULL;
			s->right = NULL;

		}
		//要让我们新节点入队,进入缓存,等待分配双亲的left域和right域
		Q[++rear] = s;

		if (rear == 1)
		{
			root = s;
		}
		else {
			if (s != NULL && Q[front]) {
				if (rear % 2 == 0) {
					Q[front]->left = s;
				}
				else {
					Q[front]->right = s;
				}
			}
			if (rear % 2 == 1)
				front++;
		}




		i++;
	}
	return root;
}




//树遍历问题:非线性结构的输出问题:前序,中序,后序
void preorder(BitTree* t) {
	if (t) {
		//根左右
		printf("%d ", t->data);
		preorder(t->left);
		preorder(t->right);
	}
}

void NonRecrvPreOrder(BitTree* root) {
	LinkStack* pTop = NULL;
	BitTreeNode TempNode;//用来接栈中对应的二叉树节点的
	pTop = InitLkStack();
	pTop = PushLkStack(pTop, root);

	while (!IsEmptyLkMazeStack(pTop)) {
		pTop = PopLkStack(pTop, &TempNode);
		if (&TempNode != NULL) {
			printf("%d ", TempNode.data);
			//对右子树压栈
			pTop = PushLkStack(pTop, TempNode.right);
			pTop = PushLkStack(pTop, TempNode.left);
		}
	}

}


void inorder(BitTree *t) {
	if (t) {
		inorder(t->left);
		printf("%d ", t->data);
		inorder(t->right);
	}
}

/*
中序就是左根右,这就意味着,当我们从根节点出发,应当首先走向左孩子
,而根的左孩子,是根的左子树的根节点,因此
又必须,继续访问其左孩子,直到左孩子为空
当这个节点访问之后,按照相同的规则,访问其右子树
我们借助的栈,就应该按照左根右来压栈
对于任意节点p
1 如果其左孩子不为空,则将p入栈,然后将p的左孩子置为当前的p,然后
再对p进行相同的处理
2 若左孩子为空,则取栈顶元素进行出栈操作,访问当前栈顶节点,然后
将当前的p置为栈顶节点的右孩子
3 直到p为NULL为止
*/
void NonRecvInorder(BitTree *root) {
	LinkStack* pTop = NULL;
	BitTreeNode TempNode;//用来接收栈中的对应的二叉树的节点

	pTop = InitLkStack();
	BitTree *p = root;
	while (p != NULL || !IsEmptyLkMazeStack(pTop)) {
		while (p != NULL) {
			pTop = PushLkStack(pTop, p);
			p = p->left;
		}
		if (!IsEmptyLkMazeStack(pTop)) {
			printf("%d ", pTop->data.data);
			pTop = PopLkStack(pTop, &TempNode);//当我们把左子树的左叶子节点走完以后,
			//此时栈顶一定是这个左孩子的根,所以根据左根右,这个根要出栈
			p = &TempNode;
			p = p->right;//走向根的右孩子 
		}
	}
}




void postorder(BitTree* t) {
	if (t) {
		//左右根
		postorder(t->left);
		postorder(t->right);
		printf("%d ", t->data);
	}
}

/*
后序:左右根
根节点只有在左孩子和右孩子都访问以后才能访问
因此,对于任何一个节点,都将其入栈,如果p不存在左孩子和右孩子,直接访问
或者p存在左孩子或者右孩子,但是他的左孩子和右孩子都已经被访问过了
同样的,这个节点也可以直接访问.
如果,上述条件都不满足,就要将这个p的右孩子,左孩子依次入栈
这样就能保证,每次取栈顶元素的时候,左孩子都在右孩子之前被访问,
左右孩子访问之后,根才被访问
*/

void NonRecvPostorder(BitTree* root) {
	BitTree* cur;//当前节点
	BitTree* pre = NULL;//前一次访问的节点

	LinkStack* pTop = NULL;
	BitTreeNode TempNode;
	pTop = InitLkStack();
	pTop = PushLkStack(pTop, root);

	while (!IsEmptyLkMazeStack(pTop)) {
		cur = &pTop->data;//拿到栈中深拷贝获得的节点
		//在访问中,由于我们是深拷贝树的节点,因此,pre和cur的地址不一样,我们只能比较器元素内容
		//小技巧:利用短路,确保程序的可靠性
		if ((cur->left == NULL && cur->right == NULL) ||
			(pre != NULL && ((cur->left  && pre->data == cur->left->data) || (cur->right && pre->data == cur->right->data)))
			) {
			printf("%d ", cur->data);
			pTop = PopLkStack(pTop, &TempNode);
			pre = &TempNode;
		}
		else {
			if (cur->right != NULL)
				pTop = PushLkStack(pTop, cur->right);
			if (cur->left != NULL)
				pTop = PushLkStack(pTop, cur->left);
		}
	}
}



int main(void) {

	int arr[17] = { 0,6,3,8,
		2,5,7,9,
		0,0,4,0,
		0,0,0,10,
		-999
	};
	BitTree *root = CreateBinTree(arr);
	/*printf("递归形式的先跟遍历\n");
	preorder(root);
	printf("\n非递归形式的先跟遍历\n");
	NonRecrvPreOrder(root);*/
	//NonRecvInorder(root);
	printf("递归形式的后根遍历\n");
	postorder(root);
	printf("\n非递归形式的后根遍历\n");
	NonRecvPostorder(root);
	getchar();
	return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值