二叉树的实现以及递归遍历的精确模拟

本文详细解析了二叉树后序遍历的递归及非递归实现过程,通过具体的例子说明了如何利用堆栈来模拟后序遍历的算法流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于一个二叉树,我们来精确模拟他的递归遍历过程

后序递归遍历的代码如下:

void posttravel(BinTree t){
    
    if(!t){
        posttravel(t->left);
        posttravel(t->right);
        printf("%c "t->data);
    }
}
我们知道,递归遍历要借助堆栈,并且前序、中序、后序遍历经过节点的次序是一样的,不同的是在不同的时刻访问他们(即打印出来),对于下面的一颗二叉树,我们按照上面的递归代码走一遍


首先,先将A入栈(A并没有执行完,需要先保存起来),

栈中是这样:

A |

执行posttravel(t->left), 

A | B

执行posttravel(t->left), 

A | B | D

执行posttravel(t->left)

A | B | D | NULL;

由于此时的t = NULL. 所以退出posttravel(t->left), 退到上一层,D的左子树执行完了

D先出栈再进栈, t = D,执行posttravel(t->right)

A | B | D | F 

由于F的左右子树都空,就不详述了,D最后出栈,返回上一层, t = D,此时D的左右子树都已经执行完了,D出栈

从D我们可以看到,每个元素都是两次进栈,两次入栈的。第一次遇到D,D第一次入栈,遍历完他的左子树后,D第一次出栈,要遍历他的右子树前,D第二次入栈,遍历完他的右子树后,D第二次出栈

一下的过程相同,有兴趣可以自己走一遍。

下面我们就根据这个过程用非递归方式精确模拟遍历二叉树的过程,注意,这里的遍历单单指遍历二叉树的每个节点,并不是要访问(打印)它,我们要知道,无论哪种遍历方式,走过的路线是一样的,只是打印的时间不同而已.


enum State{ 
	start, return_from_left, return_from_right
	// 访问左子树、右子树前都要经过根节点,我们以这两次“经过”为标准,将根节点划分为访问了自己,访问完左子树,访问完
	// 右子树,即过程是:第一次经过自己->访问左子树->第二次经过自己->访问右子树-第三次经过自己(返回上层)
};

typedef struct StackElem{
	enum State state; // 节点状态 
	BinTree t; // 节点 
}StackElem;

void postorder(BinTree t){
	// if(!t) return;
	StackElem item = {start, t}; // 根节点 
	stack s = createstack(MAX); // 遍历要借助堆栈实现 

	while(1){ // 退出条件是所有节点都遍历完(每个节点两进两出) 
		if(item.t){
			if(item.state == start){ // 遍历左子树
				push(s, item);
				item.t = item.t->left;
				item.state = start; 
			}else if(item.state == return_from_left){ // 遍历右子树
				push(s, item);
				item.t = item.t->right;
				item.state = start;
			}else{
				// 返回上一层
				// 左(右)子树遍历完后返回他的根节点(上一层) 
				if(!isempty(s)){
					item = pop(s);
					item.state++;
				}else{
					break;
				}
			}
		}else{
			// 当前为空的话,也要返回上一层 
			if(!isempty(s)){
				item = pop(s);
				item.state++;
			}else{
				break;
			}
		}
	}
	return;
}

下面是一个后序遍历的具体实现:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define ElementType char
#define MAX 50
enum State{ 
	start, return_from_left, return_from_right
	// 访问左子树、右子树前都要经过根节点,我们以这两次“经过”为标准,将根节点划分为访问了自己,访问完左子树,访问完
	// 右子树,即过程是:第一次经过自己->访问左子树->第二次经过自己->访问右子树-第三次经过自己(返回上层)
};
typedef struct TNode *BinTree;
struct TNode{
	ElementType data;
	struct TNode *left;
	struct TNode *right;
};

typedef struct StackElem{
	enum State state;
	BinTree t;
}StackElem;

typedef struct Stack{
	StackElem *data;
	int MAXSIZE;
	int top;
} *stack;

/*******************************/
BinTree createtree(BinTree t, bool isroot);
stack createstack(int max);
void push(stack s, StackElem se);
StackElem pop(stack s);
int isempty(stack s);
void postorder(BinTree t);
BinTree createtree(BinTree t, bool isroot);
void freetree(BinTree t);
/********************************/
int main(){

	BinTree t;
	t = createtree(t, true);
	postorder(t);
	freetree(t);


	return 0;
}

// 创建空栈
stack createstack(int max){
	stack s = (stack)malloc(sizeof(struct Stack));
	s->data = (StackElem *)malloc(sizeof(StackElem) *max);
	s->MAXSIZE = max;
	s->top = -1;

	return s;
}

// 进栈
void push(stack s, StackElem se){
	if(s->top+1 == s->MAXSIZE)
		return;
	s->data[++(s->top)] = se;
}

// 出栈
StackElem pop(stack s){
	StackElem tmp = s->data[s->top];
	--s->top;

	return tmp;
}

// 判空
int isempty(stack s){
	return s->top == -1;
}

// 后序遍历
void postorder(BinTree t){
	// if(!t) return;
	StackElem item = {start, t};
	stack s = createstack(MAX);

	while(1){
		if(item.t){
			if(item.state == start){ // 遍历左子树
				push(s, item);
				item.t = item.t->left;
				item.state = start;
			}else if(item.state == return_from_left){ // 遍历右子树
				push(s, item);
				item.t = item.t->right;
				item.state = start;
			}else{
				// 返回上一层
				printf("%c ", item.t->data);
				if(!isempty(s)){
					item = pop(s);
					item.state++;
				}else{
					break;
				}
			}
		}else{

			if(!isempty(s)){
				item = pop(s);
				item.state++;
			}else{
				break;
			}
		}
	}
	return;
}

// 建立二叉树
BinTree createtree(BinTree t, bool isroot){
	ElementType data;
	if(isroot){
		printf("root is:");
	}
	fflush(stdin);
	scanf("%c", &data);
    fflush(stdin);
	if(data != '#'){
        isroot = false;
		t = (BinTree )malloc(sizeof(struct TNode));
		t->data = data;
		t->left = NULL; t->right = NULL;
		printf("%c s left child is : ", t->data);
		t->left = createtree(t->left, isroot);
		printf("%c s right child is : ", t->data);
		t->right = createtree(t->right, isroot);
	}

	return t;
}

// 释放二叉树
void freetree(BinTree t){
	if(!t){
		freetree(t->left);
		freetree(t->right);
		free(t);
	}

	return;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值