二叉树的遍历(递归和非递归)

二叉树的遍历:

按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。

二叉树的遍历方法

深度遍历和层次遍历

深度遍历-从二叉树的定义知,一棵二叉树由三部分组成:根结点、左子树和右子树。若规定D,L,R分别代表“访问根结点”、“遍历根结点的左子树”和“遍历根结点的右子树”,根据遍历算法对访问根结点处理的位置,称下面三种遍历算法分别为先序/前序遍历(DLR)、中序遍历(LDR)和后序遍历(LRD)(先根、中根、后根 )。

结构体定义

​typedef char ElemType;

typedef struct TreeNode {

ElemType data;

struct TreeNode* Lchild;//左结点

struct TreeNode* Rchild;//右结点

}TreeNode;

typedef TreeNode* BiTree;

​

前序遍历

递归算法

若二叉树为空则算法结束;

若二叉树不为空,则:

访问根节点;

前序遍历根节点的左子树;

前序遍历根节点的右子树。

流程图:

void PreOrder(BiTree T) {
	//根结点为空,退出 
	if (T == NULL) {
		return;
	}
	printf("%c	", T->data);//打印根结点的数据
	PreOrder(T->Lchild);//根结点的左子树
	PreOrder(T->Rchild);//根结点的右子树 
}

非递归算法

借助堆栈的循环结构算法(使用栈存放未访问的结点)

设T是指向二叉树根结点的指针变量,非递归算法:

对于任一结点均可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问完其左子树时,再访问它的右子树。因此其处理过程如下:

初始p=root,对于任一结点p:

1)若P不为空,执行下面循环:

访问结点P,并将结点P入栈;

将P的左孩子置为当前的结点P;//遍历左子树

2)若P为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P;//遍历右子树

3) 1)和2)循环执行,直到P为NULL并且栈为空,则遍历结束

流程图:

void PreOrder(BiTree T) { 
	SeqStack st;
	Stackinit(&st);
	BiTree p = T;
	while (p != NULL || Stackempty(st)) {
		while (p != NULL) {
			printf("%c	", p->data); //访问根
			Stackpush(&st, p);//入栈
			p = p->Lchild; 	//遍历左子树
		}
		if (Stackempty(st)) {
			Stackpop(&st, &p);//出栈
			p = p->Rchild; 	 //遍历右子树
		}
	}
}

前序遍历为ABDGCEF

中序遍历

先遍历左结点,再遍历根结点,最后遍历

递归算法

若二叉树为空则算法结束;

若二叉树不为空:

(1)中序遍历根结点的左子树;

(2)访问根结点;

(3)中序遍历根结点的右子树。

流程图:

void InOrder(BiTree T) {
	/*中序遍历二叉树t */
	if (T != NULL) {
		InOrder(T->Lchild);
		printf("%c	", T->data);
		InOrder(T->Rchild);
	}
}

非递归算法

若二叉树为空,则返回;

二叉树不为空:令p=T,

当p非空或者堆栈非空,执行下述循环:

p不为空,p进栈, p=p->Lchild ,直到p为空为止;(此步为循环)

p为空,出栈给p,访问p所指向的结点, p=p->tRchild ;

流程图:

void InOrder(BiTree T) { //顺序栈
	SeqStack st;
	Stackinit(&st);
	BiTree p = T;
	while (p != NULL || Stackempty(st)) {
		while (p != NULL) {     //遍历左子树
			Stackpush(&st, p);//入栈
			p = p->Lchild;//左结点
		}
		if (Stackempty(st)) {
			Stackpop(&st, &p);//出栈
			printf("%c	", p->data);   //访问根结点
			p = p->Rchild; //通过下一次循环实现右子树遍历
		}
	}
}

中序遍历:DGBAECF

后序遍历:

递归算法

若二叉树为空则算法结束;否则:

(1)后序遍历根结点的左子树;

(2)后序遍历根结点的右子树;

(3)访问根结点。

流程图:

void PostOrder(BiTree T) {
	/*后序遍历二叉树t */
	if (T != NULL) {
		PostOrder(T->Lchild);
		PostOrder(T->Rchild);
		printf("%c	", T->data);
	}
}

非递归算法

如果根结点为空,结束

左子树不为空时,curr->Lchild入栈

若为空,得到栈顶元素;

若栈顶元素的右子树不为空且未被访问过,作为新的根结点

反之,栈顶元素出栈,打印数据,标记最近访问的元素

流程图:

void PostOrder(BiTree T) {
	SeqStack st;
	Stackinit(&st);
	BiTree curr = T;//当前结点
	BiTree visit = T;//被访问过的结点
	while (curr != NULL || Stackempty(st)) {//如果根结点不为空,同时栈不为空
		while (curr != NULL) {     //遍历左子树
			Stackpush(&st, curr);//入栈
			curr = curr->Lchild;//左结点
		}
		//得到栈顶元素,不出栈
		Stacktop(st, &curr);
		if (curr->Rchild && curr->Rchild != visit) {//右结点存在同时结点未被访问过
			curr = curr->Rchild;//指针指向右结点并进入循环,使得右结点入栈
		}
		else {//右结点不存在 栈顶元素出栈
			Stackpop(&st, &curr);
			printf("%c	", curr->data);
			visit = curr;//标记最近访问元素
			curr = NULL;//以该节点为根的子树全部访问完成
		}
	}

}

后序遍历:G D B E F C A

层序遍历

层序遍历的要求是:按二叉树的层次顺序(即从根结点层至叶结点层),同一层中按先左子树再右子树的次序遍历二叉树。

层序遍历的特点:在所有未被访问结点的集合中,排列在已访问结点集合中最前面结点的左子树的根结点将最先被访问,然后是该结点的右子树的根结点。

这样,可以把待访问的结点放在一个队列中,当队头待访问结点出队列访问时,把该结点的左右孩子依次放入队列等待访问。那么,下一层未被访问结点的访问次序就可以由存放在队列中的上一层待访问结点的出队列次序决定。因此可以借助队列实现二叉树的层序遍历

二叉树的层序遍历算法如下:

初始化设置一个队列;

把根结点指针入队列;

当队列非空时,循环执行步骤(3.a)到步骤(3.c);

(3.a)出队列取得一个结点指针,访问该结点;

3.b)若该结点的左子树非空,则将该结点的左子树指针入队列;

3.c)若该结点的右子树非空,则将该结点的右子树指针入队列;

流程图:

 

void  LevelOrderTraverse( BiTree T){    

SeqCQueue sq;

    Queueinit(&sq);

    BiTree p=T ;

if  (p!=NULL) {

Queueappend(&sq, p);    /*   根结点入队  */

while (Queueempty(sq)) {

Queuepop(&sq, &p);

printf("%c        ",p->data);

if (p->Lchild!=NULL)

    Queueappend(&sq,p-> Lchild); /* 左结点入队 */

 if (p->Rchild!=NULL)

    Queueappend(&sq, p->Rchild);  /*右结点入队 */

  }

}

}

层序遍历:ABCDEFG

完整的代码

前中后序:

#include<stdio.h>
#include<string.h>
#include<malloc.h>
typedef char ElemType;
typedef struct TreeNode {
	ElemType data;
	struct TreeNode* Lchild;//左结点
	struct TreeNode* Rchild;//右结点 
}TreeNode;
typedef TreeNode* BiTree;

#define MaxStackSize 100
typedef BiTree DataType;

typedef struct {
	DataType stack[MaxStackSize];
	int top;
}SeqStack;

//初始化
void Stackinit(SeqStack* S) {
	S->top = 0;//赋初值,使得头指针指向栈顶的下标值 
}

//非空否
int Stackempty(SeqStack S) {
	if (S.top <= 0) {
		return 0;//栈为空 
	}
	else {
		return 1;//栈不为空 
	}
}

//入栈
int Stackpush(SeqStack* S, BiTree p) {
	//先判断栈内是否为满
	if (S->top >= MaxStackSize) {
		printf("堆栈已满无法放入\n");
		return 0;//入栈失败 
	}
	else {
		S->stack[S->top] = p;//在数组的末端插入x 
		S->top++;//更新指针位置 
		return 1;//入栈成功 
	}
}

//出栈
int Stackpop(SeqStack* S, BiTree* p) {
	//先判断栈是否为空
	if (S->top <= 0) {
		printf("堆栈已空,无元素出栈\n");
		return 0;//出栈失败 
	}
	else {
		S->top--;
		*p = S->stack[S->top];//用x存储出栈的元素
		return 1;//元素出栈成功 
	}
}

//取栈顶元素
int Stacktop(SeqStack S, BiTree *p) {
	if (S.top <= 0) {
		printf("堆栈已空,无元素出栈\n");
		return 0;//无栈顶元素 
	}
	else {
		*p = S.stack[S.top - 1];//存储栈顶元素 
		return 1;
	}
}

//前序遍历
void PreOrder1(BiTree T) {
	//根结点为空,退出 
	if (T == NULL) {
		return;
	}
	printf("%c ", T->data);//打印根结点的数据
	PreOrder1(T->Lchild);//根结点的左子树
	PreOrder1(T->Rchild);//根结点的右子树 
}




void PreOrder2(BiTree T) { //非递归前序遍历2 
	SeqStack st;
	Stackinit(&st);
	BiTree p = T;
	while (p != NULL || Stackempty(st)) {
		while (p != NULL) {
			printf("%c ", p->data); //访问根
			Stackpush(&st, p);
			p = p->Lchild; 	//遍历左子树
		}
		if (Stackempty(st)) {
			Stackpop(&st, &p);
			p = p->Rchild; 	 //遍历左子树
		}
	}
}

//中序遍历
void InOrder1(BiTree T) {
	/*中序遍历二叉树t */
	if (T != NULL) {
		InOrder1(T->Lchild);
		printf("%c ", T->data);
		InOrder1(T->Rchild);
	}
}

void InOrder2(BiTree T) { //顺序栈
	SeqStack st;
	Stackinit(&st);
	BiTree p = T;
	while (p != NULL || Stackempty(st)) {
		while (p != NULL) {     //遍历左子树
			Stackpush(&st, p);//入栈
			p = p->Lchild;//左结点
		}
		if (Stackempty(st)) {
			Stackpop(&st, &p);//出栈
			printf("%c ", p->data);   //访问根结点
			p = p->Rchild; //通过下一次循环实现右子树遍历
		}
	}
}

//后序遍历
void PostOrder1(BiTree T) {
	/*后序遍历二叉树t */
	if (T != NULL) {
		PostOrder1(T->Lchild);
		PostOrder1(T->Rchild);
		printf("%c ", T->data);
	}
}

void PostOrder2(BiTree T) {
	SeqStack st;
	Stackinit(&st);
	BiTree curr = T;//当前结点
	BiTree visit = T;//被访问过的结点
	while (curr != NULL || Stackempty(st)) {//如果根结点不为空,同时栈不为空
		while (curr != NULL) {     //遍历左子树
			Stackpush(&st, curr);//入栈
			curr = curr->Lchild;//左结点
		}
		//得到栈顶元素,不出栈
		Stacktop(st, &curr);
		if (curr->Rchild && curr->Rchild != visit) {//右结点存在同时结点未被访问过
			curr = curr->Rchild;//指针指向右结点并进入循环,使得右结点入栈
		}
		else {//右结点不存在 栈顶元素出栈
			Stackpop(&st, &curr);
			printf("%c ", curr->data);
			visit = curr;//标记最近访问元素
			curr = NULL;//以该节点为根的子树全部访问完成
		}
	}

}

int createTree(BiTree* T) {
	ElemType ch;
	//输入二叉树各个结点的数值 其中#表示为空 
	scanf("%c", &ch);
	if (ch == '#') {
		*T = NULL;
	}
	else {
		(*T) = (BiTree)malloc(sizeof(TreeNode));
		if ((*T) != NULL) {
			(*T)->data = ch;
			//创建左子树
			createTree(&(*T)->Lchild);
			//创建右子树
			createTree(&(*T)->Rchild);
		}

	}
	return 1;
}

int main() {
	BiTree T = NULL;
	createTree(&T);
	printf("输出前序遍历的结果:");	
	PreOrder1(T);
	printf("\n");
	PreOrder2(T);
	printf("\n");
	printf("输出中序遍历的结果:");
	InOrder1(T);
	printf("\n");
	InOrder2(T);
	printf("\n");
	printf("输出后序遍历的结果:");
	PostOrder1(T);
	printf("\n");
	PostOrder2(T);
	printf("\n");
	return 0;
}


层序遍历的完整代码

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

/* run this program using the console pauser or add your own getch, system("pause") or input loop */

typedef char ElemType;
typedef struct TreeNode {
	ElemType data;
	struct TreeNode* Lchild;//左结点
	struct TreeNode* Rchild;//右结点 
}TreeNode;
typedef TreeNode* BiTree;
#define Maxqueuesize 100
typedef BiTree DataType;

typedef struct {
	DataType queue[Maxqueuesize];
	int front;
	int count;
}SeqCQueue;


//初始化循环队列
void Queueinit(SeqCQueue* Q) {
	Q->front = 0;//定义初始队头指针下标值
	Q->count = 0;//计数器置空 
}

//判断队列是否为空
int Queueempty(SeqCQueue Q) {
	if (Q.count != 0) {
		return 1;//不是为空 
	}
	else {
		return 0;//队列为空 
	}
}

//入队列
int Queueappend(SeqCQueue* Q, DataType x) {
	if (Q->count >= Maxqueuesize) {
		printf("队列已满\n");
		return 0;//插入失败 
	}
	else {
		Q->queue[(Q->count + Q->front) % Maxqueuesize] = x;
		Q->count++;
		return 1;
	}
}

//出队列
int Queuepop(SeqCQueue* Q, DataType* x) {
	if (Q->count <= 0) {
		printf("队列为空\n");
		return 0;
	}
	else {
		*x = Q->queue[Q->front];
		Q->front = (Q->front + 1) % Maxqueuesize;
		Q->count--;
		return 1;
	}
}


void  LevelOrderTraverse( BiTree T){     
		SeqCQueue sq;
	    Queueinit(&sq);
	    BiTree p=T ;
		if  (p!=NULL) {
			Queueappend(&sq, p);    /*   根结点入队  */
			while (Queueempty(sq)) {
				Queuepop(&sq, &p);
				printf("%c	",p->data);
				if (p->Lchild!=NULL)
				    Queueappend(&sq,p-> Lchild); /* 左结点入队 */
				 if (p->Rchild!=NULL)
				    Queueappend(&sq, p->Rchild);  /*右结点入队 */
				  }
			}
}

int createTree(BiTree* T) {
	ElemType ch;
	//输入二叉树各个结点的数值 其中#表示为空 
	scanf("%c", &ch);
	if (ch == '#') {
		*T = NULL;
	}
	else {
		(*T) = (BiTree)malloc(sizeof(TreeNode));
		if ((*T) != NULL) {
			(*T)->data = ch;
			//创建左子树
			createTree(&(*T)->Lchild);
			//创建右子树
			createTree(&(*T)->Rchild);
		}

	}
	return 1;
}

int main(int argc, char *argv[]) {
	BiTree T;
	createTree(&T) ;
	LevelOrderTraverse(T);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值