二叉树的三种遍历算法的实现(前序、中序、后序)递归与非递归

二叉树的三种遍历算法的实现(前序、中序、后序)递归与非递归

1、二叉树的定义

二叉树是n(n>=0)个数据元素的有限集,含有唯一的称为根的元素,且,其余元素分成两个互不相交的子集,每一个子集本身也是一个二叉树,分别称为左子树右子树。集合为空的二叉树简称为空树,二叉树中的元素也称为节点。这是一个递归的定义。

2、二叉树的存储结构:

2.1 顺序存储结构

typedef struct{
TElemType *data;//存储空间的基地址
int nodenum;//树中节点的个数
}SqBiTree;

完全二叉树存储方式 完全二叉树的存储方式
一般二叉树存储方式
一般二叉树的存储方式

由此可以看出这种存储方式比较浪费空间。

2.2 链式存储结构

节点的逻辑结构
在这里插入图片描述

typedef struct BiTNode{
 TElemType data;
 struct BiTNode *lchild ,*rchild;
 }BiTNode,*BiTree;

3、二叉树的遍历方式

3.1先序遍历二叉树(递归实现)

若二叉树为空,则空操作否则
(1)访问根节点
(2)先序遍历左子树
(3)先序遍历右子树

void Preorder(BiTree T,void(*visit)(BiTree))
{
	if(T)
	{	
		visit(T);
		Preorder(T->lchild,visit);
		Preorder(T->rchild,visit);
		}
}

3.2中序遍历二叉树(递归实现)

若二叉树为空,则空操作否则
(1)中序遍历左子树
(2)访问根节点
(3)中遍历右子树

void Preorder(BiTree T,void(*visit)(BiTree))
{
	if(T)
	{	
	    Preorder(T->lchild,visit);
		visit(T);
		Preorder(T->rchild,visit);
		}
}

3.3后序遍历二叉树(递归实现)

若二叉树为空,则空操作否则
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根节点

4、二叉树三种遍历的非递归实现

4.1算法说明

当二叉树不为空时,中序遍历二叉树的任务可以由三项子任务组成,即遍历左子树、访问根节点、遍历右子树,其中第一和第三项任务比较复杂,但可以大事化小,继续分解为两项较小的遍历任务和一项访问任务,中间的这项任务比较单纯,可以直接处理,小事化了。
可以将栈看成存放任务书的柜子,初始化时,栈中只有一项任务,即中序遍历二叉树,之后每从栈中取出一份任务书,即将任务复杂程度进行相应的处理,直到栈为空。

4.2伪代码

typedef enum {Travel = 1,Visit = 0}TaskType;//Travel = 1:工作状态为遍历。Visit=0:工作状态为访问
typedef struct{
	BiTree ptr;
	TaskType task
}SElemType;//栈元素 
void InOrder(BiTree T,void(*visit)(TElemType e))
{//利用栈实现中序遍历二叉树,T为指向二叉树根节点的头指针
	InitStack(&S);//初始化栈
	e.ptr = T;//e为栈元素
	e.task = Travel;
	if(T)
		Push(&S,e);//布置初始任务
	while(!IsEmpty(S))
	{//每次处理一项任务
		Pop(&S,&e);
		if(e.task == Visit)
			visit(e.ptr->data);//处理访问任务
		else
		{
			if(e.ptr)//处理非空树的遍历任务
			{
				p = e.ptr;
				e.ptr = p->rchild;
				Push(&S,e);//最不迫切的任务右子树进栈
				e.ptr = p;
				e.task = Visit;
				Push(&S,e);//处理访问任务的工作状态和节点指针域进栈
				e.ptr = p->lchild;
				e.task = Travel;
				Push(&S,e);//迫切的任务遍历左子树进栈
			}
		}
	}
	
	
}

利用访问任务的进栈语句的不位置可以写出前序后序的非递归算法。

5完整代码

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

#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
#define OK 1
#define ERROR 0

#define INIT_SIZE 20
#define INCREMENT_SIZE 5

typedef int Status;
typedef int TElemType;
//存储结构 

typedef struct BiNode{
	TElemType data;
	struct BiNode *lchild, *rchild;
}BiNode, *BiTree;

typedef enum {Travel = 1,Visit = 0}TaskType;

typedef struct{
	BiTree ptr;
	TaskType task
}SElemType;//栈元素 

typedef struct{
	SElemType *top;
	SElemType *base;
	int size;	
}Stack;//栈的数据结构 
//初始化栈
Status InitStack(Stack *S)
{
	S->base = (SElemType *)malloc(sizeof(SElemType)*INIT_SIZE);
	if(!S->base)exit(OVERFLOW);
	S->top = S->base;
	S->size = INIT_SIZE;
	return OK;
}

 //判断是否为空
Status IsEmpty(Stack S)
  {
  	if(S.base==S.top)return TRUE;
  	return FALSE;
   } 
//进入栈
 Status Push(Stack *S,SElemType e)//*S->top++=e
 {
 	if((S->top - S->base) / sizeof(SElemType)>=S->size)
 	{
 		S->base = (SElemType*)realloc(S->base,(S->size+INCREMENT_SIZE)*sizeof(SElemType));
 		if(!S->base)exit(OVERFLOW);
 		S->top = S->base + S->size;//从新申请了内存地址发生了变化 
 		S->size += INCREMENT_SIZE;
	 }
	 *S->top = e;//先取*s->top 在++//*++S->top先加再取值; 
	 S->top++;
	 return OK;
  } 
  
  //pop
Status Pop(Stack *S, SElemType *e)
{
	if(S->top == S->base)return ERROR;
	*e = *--S->top;
	return OK;
 } 

//创建二叉树(输入0结束) 
Status CreateBiTree(BiTree *T) 
{
	TElemType s;
	scanf("%d",&s);
	if(s==0)
		*T=NULL;
	else{
		
		*T = (BiTree)malloc(sizeof(BiNode));
		if(!T)
		{
			return OVERFLOW;
		}
		(*T)->data = s;
		CreateBiTree(&(*T)->lchild);//修改指针的值使其指向创建的值 
		CreateBiTree(&(*T)->rchild);
		}
		return OK;	
}
//访问元素
void visit(TElemType e)
{
	printf("%d ",e);
 } 
//先序遍历递归实现
Status PreOrderTraverse(BiTree T,void(*visit)(TElemType e))
{
	if(T)
	{
		(*visit)(T->data);
		PreOrderTraverse(T->lchild,visit) ;
		PreOrderTraverse(T->rchild,visit);
	}
 } 
 //中序遍历 
 
 Status InOrderTraverse(BiTree T,void(*visit)(TElemType e))
{
	if(T)
	{
		InOrderTraverse(T->lchild,visit);
		(*visit)(T->data);
		InOrderTraverse(T->rchild,visit);
	}
 } 
 // houxu
 
 Status PostOrderTraverse(BiTree T,void(*visit)(TElemType e))
{
	if(T)
	{
		PostOrderTraverse(T->lchild,visit);
		PostOrderTraverse(T->rchild,visit);
		(*visit)(T->data);
	}
 } 
 
//前序遍历非递归
Status  PreOrder(BiTree T,void(*visit)(TElemType e))
{
	Stack S;
	InitStack(&S);
	BiTree p; 
	SElemType e;
	e.ptr = T;
	e.task = Travel;
	if(T)
		Push(&S,e);
	while(!IsEmpty(S))
	{ 
		Pop(&S,&e);
		if(e.task == Visit)
			visit(e.ptr->data);
		else
		{
			if(e.ptr)
			{
		    	p = e.ptr;
				e.ptr = p->rchild;
				e.task = Travel;
				Push(&S,e);
				e.ptr = p->lchild;
				e.task = Travel;
				Push(&S,e);
				e.ptr = p;
				e.task = Visit;
				Push(&S,e);	
			}
			
		}
		
	}
	
	
} 
//中序遍历非递归
Status  InOrder(BiTree T,void(*visit)(TElemType e))
{
	Stack S;
	InitStack(&S);
	BiTree p; 
	SElemType e;
	e.ptr = T;
	e.task = Travel;
	if(T)
		Push(&S,e);
	while(!IsEmpty(S))
	{
		Pop(&S,&e);
		if(e.task == Visit)
			visit(e.ptr->data);
		else
		{
			if(e.ptr)
			{
				p = e.ptr;
				e.ptr = p->rchild;
				Push(&S,e);
				e.ptr = p;
				e.task = Visit;
				Push(&S,e);
				e.ptr = p->lchild;
				e.task = Travel;
				Push(&S,e);
			}
		}
	}
	
	
}

//houxu非递归
Status  PostOrder(BiTree T,void(*visit)(TElemType e))
{
	Stack S;
	InitStack(&S);
	BiTree p; 
	SElemType e;
	e.ptr = T;
	e.task = Travel;
	if(T)
		Push(&S,e);
	while(!IsEmpty(S))
	{
		Pop(&S,&e);
		if(e.task == Visit)
			visit(e.ptr->data);
		else
		{
			if(e.ptr)
			{
				e.task = Visit;
				Push(&S,e);
				p = e.ptr;
				e.ptr = p->rchild;
				e.task = Travel;
				Push(&S,e);
				e.ptr = p->lchild;
				e.task = Travel;
				Push(&S,e);
			}
		}
	}
	
	
}
 
 int main()
{
    BiTree T;
    printf("创建树,输入0为空树:\n");
    CreateBiTree(&T);
    printf("先序遍历递归:");
    PreOrderTraverse(T, *visit);
    printf("\n中序遍历递归:");
    InOrderTraverse(T, *visit);
    printf("\n后序遍历递归:");
    PostOrderTraverse(T, *visit);
    printf("\n");
    
    printf("\n前序非递归算法: ");
    PreOrder( T,*visit);
    printf("\n中序非递归算法: ");
    InOrder( T,*visit);
    printf("\n后序非递归算法: ");
    PostOrder( T,*visit);
    

    return 0;
}

输入数据

在这里插入图片描述在这里插入图片描述

  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 二叉树中序前序后序遍历算法分为递归非递归两种方法。 递归遍历算法: 1. 中序遍历:先遍历左子树,再访问根节点,最后遍历右子树。 2. 前序遍历:先访问根节点,再遍历左子树,最后遍历右子树。 3. 后序遍历:先遍历左子树,再遍历右子树,最后访问根节点。 非递归遍历算法: 1. 中序遍历:使用栈来模拟递归过程,先将根节点入栈,然后将左子树的所有节点入栈,直到遇到叶子节点,然后出栈并访问该节点,再将右子树的所有节点入栈,重复上述过程。 2. 前序遍历:使用栈来模拟递归过程,先将根节点入栈,然后出栈并访问该节点,再将右子树入栈,最后将左子树入栈,重复上述过程。 3. 后序遍历:使用栈来模拟递归过程,先将根节点入栈,然后将左子树的所有节点入栈,直到遇到叶子节点,然后将右子树的所有节点入栈,最后出栈并访问该节点,重复上述过程。需要使用一个辅助栈来记录已经访问过的节点。 ### 回答2: 二叉树中序前序后序遍历是树的三种基本遍历方法,他们都有递归非递归两种算法。 1. 中序遍历 中序遍历是先访问左子树,再访问根节点,最后访问右子树。递归遍历可以先递归访问左子树,然后输出根节点,继续递归访问右子树。非递归遍历可以用栈进行模拟,从根节点开始,先访问左子树,遇到左子树都入栈,然后输出根节点,最后遍历右子树。 2. 前序遍历 前序遍历是先访问根节点,再访问左子树,最后访问右子树。递归遍历可以先输出根节点,然后递归访问左子树和右子树。非递归遍历可以用栈进行模拟,从根节点开始,先输出根节点,然后将右子树入栈,最后遍历左子树。 3. 后序遍历 后序遍历是先访问左子树,再访问右子树,最后访问根节点。递归遍历可以先递归访问左子树和右子树,最后输出根节点。非递归遍历可以用栈进行模拟,在遍历每个节点的时候都要记录下该节点是否已经被访问过了,当左右子树都被访问过的时候,再输出根节点。 总之,递归遍历的代码比较简单,但是会使用到函数调用栈,容易导致栈溢出,所以针对比较大的二叉树,我们通常使用非递归遍历非递归遍历可以使用栈来模拟递归过程,但是需要注意栈空的时候需要退出循环。 ### 回答3: 二叉树遍历是指以某种顺序依次访问二叉树中所有结点,是二叉树最常用的操作之一。常见的遍历方式有中序遍历前序遍历后序遍历。这三种遍历方式都可以采用递归非递归算法实现。 1. 中序遍历 中序遍历的顺序是左孩子-根节点-右孩子。递归实现中序遍历: ``` void inorder(TreeNode* root) { if (root == nullptr) return; inorder(root->left); visit(root); inorder(root->right); } ``` 非递归实现中序遍历: ``` vector<int> inorderTraversal(TreeNode* root) { vector<int> res; stack<TreeNode*> s; TreeNode* cur = root; while (cur || !s.empty()) { while (cur) { s.push(cur); cur = cur->left; } cur = s.top(); s.pop(); res.push_back(cur->val); cur = cur->right; } return res; } ``` 2. 前序遍历 前序遍历的顺序是根节点-左孩子-右孩子。递归实现前序遍历: ``` void preorder(TreeNode* root) { if (root == nullptr) return; visit(root); preorder(root->left); preorder(root->right); } ``` 非递归实现前序遍历: ``` vector<int> preorderTraversal(TreeNode* root) { vector<int> res; if (root == nullptr) return res; stack<TreeNode*> s; s.push(root); while (!s.empty()) { TreeNode* tmp = s.top(); s.pop(); res.push_back(tmp->val); if (tmp->right) s.push(tmp->right); if (tmp->left) s.push(tmp->left); } return res; } ``` 3. 后序遍历 后序遍历的顺序是左孩子-右孩子-根节点。递归实现后序遍历: ``` void postorder(TreeNode* root) { if (root == nullptr) return; postorder(root->left); postorder(root->right); visit(root); } ``` 非递归实现后序遍历: ``` // 双栈法 vector<int> postorderTraversal(TreeNode* root) { vector<int> res; if (root == nullptr) return res; stack<TreeNode*> s1, s2; s1.push(root); while (!s1.empty()) { TreeNode* tmp = s1.top(); s1.pop(); s2.push(tmp); if (tmp->left) s1.push(tmp->left); if (tmp->right) s1.push(tmp->right); } while (!s2.empty()) { res.push_back(s2.top()->val); s2.pop(); } return res; } ``` ``` // 单栈法 vector<int> postorderTraversal(TreeNode* root) { vector<int> res; if (root == nullptr) return res; stack<TreeNode*> s; TreeNode* cur = root; TreeNode* last = nullptr; while (cur || !s.empty()) { while (cur) { s.push(cur); cur = cur->left; } cur = s.top(); if (cur->right == nullptr || cur->right == last) { res.push_back(cur->val); s.pop(); last = cur; cur = nullptr; } else { cur = cur->right; } } return res; } ``` 非递归实现需要用到栈,代码实现可能有些繁琐,但是值得熟练掌握。在实际编码中,也可使用Morris遍历实现二叉树遍历并节省空间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值