二叉树遍历(先序,中序,后序,层序)递归和非递归形式

Posted 两片空白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树遍历(先序,中序,后序,层序)递归和非递归形式相关的知识,希望对你有一定的参考价值。

二叉树的遍历是通过一定顺序来访问二叉树的所有结点。遍历的方法有四种:先序遍历,中序遍历,后序遍历和层序遍历。
其中前三种一般使用深度优先搜索(DBS)实现,而层次遍历一般用广度优先搜索(BFS)实现。

在这里插入图片描述
二叉树表示:

struct TreeNone{
	int data;
	struct TreeNone *lchild;
	struct TreeNone *rchild;
};

注意:
1.访问时访问左子树一定在右子树之前。
2.前,中,后序遍历的非递归形式都是用栈来实现的。
3.层序遍历用队列来实现。

1.先序遍历

定义

对于先序遍历,其访问二叉树的结点的顺序为:
1. 先访问根节点
2. 访问左子树
3. 访问右子树

递归

为了递归实现先序遍历首先得得到两个条件:递归出口和递归式。
其中递归式可由先序遍历的定义直接得到,递归出口为二叉树为空树。

代码有:

void preorderTraversal(struct TreeNone *root){
	if (root==NULL){//递归出口,二叉树为空
		return;
	}
	else{
		printf("%d\\n", root->data);//访问根节点
		preorderTraversal(root->lchild);//访问左子树
		preorderTraversal(root->rchild);//访问右子树
	}
}

递归理解:根据先序遍历的遍历顺序,树不为空时一开始进来(printf)就是访问根节点,再去访问左子树(一开始递归进来就是左子树的根节点),其次访问右子树(一开始递归进来就是右子树的根节点)。

非递归(栈)

结点一开始(根节点)进来先被访问,再走向左子树,将结点入栈(push),直到走到空子树,再将结点出栈(pop),出栈的结点就是原来不是空的结点,再走向右子树。
注意:右边如果为空,不会进入走向左边的循环,直接将结点出栈。

void preorderTraversal(struct TreeNone *root){
	struct TreeNode *cur = root;
	struct TreeNode *ass[100];//定义一个栈
	int top = -1;//栈顶指针
	while (cur || top != -1){//如果数不为空或者栈里还有节点
		while (cur){//一直走向左子树
			printf("%d\\n",cur->data);//访问根节点
			ass[++top] = cur;//入栈
			cur = cur->lchild;
		}
		cur = ass[top--];//出栈
		cur = cur->rchild;//往右走一个结点
	}
}

2.中序遍历

定义

对于中序遍历,其访问二叉树的结点的顺序为:
1. 先访问左子树
2. 访问根节点
3. 访问右子树

递归

跟先序递归条件相同,只不过递归式为中序的定义。
void inorderTraversal(struct TreeNone *root){
	if (root==NULL){//递归出口,二叉树为空
		return;
	}
	else{
		preorderTraversal(root->lchild);//访问左子树
		printf("%d\\n", root->data);//访问根节点
		preorderTraversal(root->rchild);//访问右子树
	}
}

递归理解:根据中序遍历的遍历顺序,树不为空时一开始进来先去遍历左子树的左子树,再访问根节点(走到最后一个不为空的叶节点,也可以理解为只有一个结点的树,所以也算是根节点)。再访问左子树的右子树。再访问右子树的左子树,再访问根节点,再访问右子树的右子树。

非递归(栈)

结点一开始走向左子树,将结点入栈(push),直到走到空子树,再将结点出栈(pop),然后访问根节点,再走向右子树,循环。与先序遍历差不多,只是访问根节点时顺序换了。

void inorderTraversal(struct TreeNone *root){
	struct TreeNode *cur = root;
	struct TreeNode *ass[100];//定义一个栈
	int top = -1;//栈顶指针
	while (cur || top != -1){//如果数不为空或者栈里还有节点
		while (cur){//一直走向左子树
			ass[++top] = cur;//入栈
			cur = cur->lchild;
		}
		cur = ass[top--];//出栈
		printf("%d\\n",cur->data);//访问根节点
		cur = cur->rchild;//往右走一个结点
	}
}

3.后序遍历

定义

对于先序遍历,其访问二叉树的结点的顺序为:
1. 先访问左子树
2. 访问右子树
3. 访问根节点

递归

递归条件相同,只是递归式变为了后序递归定义
void postorderTraversal(struct TreeNone *root){
	if (root==NULL){//递归出口,二叉树为空
		return;
	}
	else{
		preorderTraversal(root->lchild);//访问左子树
		preorderTraversal(root->rchild);//访问右子树
		printf("%d\\n", root->data);//访问根节点
	}
}

递归理解:根据后序递归遍历顺序,先访问左子树,到空树后,再访问左子树的右子树,再访问根节点。再访问右子树的左子树,到空树,再访问右子树的左子树。再访问根节点。

非递归(栈)

结点一开始往左子树的左子树遍历,并且每个结点入栈,直到空树后,放出栈里的一个结点,此时不访问,先判断是否有右子树。所以这里有两种情况。
1.有右子树:将该结点入栈,走到右节点出。
2.没有右节点,访问该节点,先用一变量保存该节点,将该节点置空。(置空为了不进入向左子树走的循环,直接出栈一节点。保存该节点是因为,出栈的结点的右子树是变量保存结点)。

void postorderTraversal(struct TreeNone *root){
	struct TreeNode *cur = root;
	struct TreeNode *ass[100];//定义一个栈
	int top = -1;//栈顶指针
	struct TreeNode temp;
	while (cur || top != -1){//如果数不为空或者栈里还有节点
		while (cur){//一直走向左子树
			ass[++top] = cur;//入栈
			cur = cur->lchild;
		}
		cur = ass[top--];//出栈
		if(cur->rchild==NULL||cur->rchild==temp){
			printf("%d\\n",cur->data);//访问根节点
			temp=cur;//用一变量保存该节点
			cur=NULL;//置空
		}
		else{
			ass[top++]=cur;//该节点入栈
			cur = cur->rchild;//往右走一个结点
		}
	}
}

4.层序遍历

定义:

从上往下,从左往右依次遍历。

代码实现(队列)

基本思路:

  1. 创建一队列
  2. 将根节点放入队列
  3. 取出队列首结点访问它
  4. 判断左子树是否为空,不为空入队。
  5. 判断右子树是否为空,不为空入队。
  6. 循环步骤3,直到队列为空。
typedef struct TreeNone *  Elementtype;
struct Queue{
	Elementtype *a;
	int front;
	int rear;
	int capacity;
};

struct Queue *creatQueue(){//队列初始化
	struct Queue *queue = (struct Queue *)malloc(sizeof(struct Queue));
	queue->a = (Elementtype *)malloc(sizeof(Elementtype)*100);
	queue->front = 0;
	queue->rear = 0;
	queue->capacity = 100;
	return queue;
}

void AddQ(struct Queue *queue, struct TreeNode *cur){//将结点入队
	queue->rear = (queue->rear + 1) % queue->capacity;//循环队列
	queue->a[queue->rear] = cur;

}
struct TreeNone *DeleteQ(struct Queue *queue){//将结点出队
	queue->front = (queue->front + 1) % queue->capacity;//循环队列
	return queue->a[queue->front];

}
bool Isempty(struct Queue *queue){//判断队列是否为空
	if (queue->front == queue->rear){
		return true;
	}
	return false;
}

void levelorderTraversal(struct TreeNone *root){
	struct TreeNone *cur = root;
	struct TreeNone *temp = NULL;//变量保存出队结点
	if (!root){
		return;
	}
	struct Queue *queue = creatQueue();//创建一队列
	AddQ(queue, cur);//将根结点入队
	while (!Isempty(queue)){
		temp = DeleteQ(queue);//出队
		printf("%d\\n", temp->data);//访问
		if (!(temp->lchild)){//左子树入队
			AddQ(queue, temp->lchild);
		}
		if (!(temp->rchild)){//右子树入队
			AddQ(queue, temp->rchild);
		}
	}

}

以上是关于二叉树遍历(先序,中序,后序,层序)递归和非递归形式的主要内容,如果未能解决你的问题,请参考以下文章

二叉树的非递归遍历(先序中序后序和层序遍历)

详解二叉树的遍历问题(前序后序中序层序遍历的递归算法及非递归算法及其详细图示)

二叉树的前序,中序,后序,层序遍历的递归和非递归实现

数据结构 递归和非递归方式实现二叉树先序中序和后序遍历

二叉树的递归和非递归遍历(前序中序后序层序)

C语言数据结构,急求在线二叉树先序中序后序递归遍历