二叉树遍历的非递归算法

这篇具有很好参考价值的文章主要介绍了二叉树遍历的非递归算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

非递归的算法主要采用的是循环出栈入栈来实现对二叉树的遍历,下面是过程分析

以下列二叉树为例:(图片来自懒猫老师《数据结构》课程相关内容)

二叉树遍历的非递归算法二叉树遍历的非递归算法

1.前序遍历

前序遍历的顺序为:根结点->左子树->右子树

基本过程:

(1)访问根结点,将根结点入栈

(2)循环逐个访问左子树,执行(1)中步骤;当访问到没有左子树的结点时,跳出循环

(3)栈不为空,根结点出栈,访问右子树

这里以A的左子树为例进行栈的变化过程说明:

二叉树遍历的非递归算法

可以总结成,没有左子树->出栈+右子树入栈;没有右子树->出栈

代码实现:

void PreOrder(BiNode *bt) { //树的前序遍历
	SqStack s;
	s = InitStack();
	BiNode *p = bt;
	while (p != NULL || StackEmpty(&s) != 1) { //当p为空,栈也为空时退出循环
		while (p != NULL) {
			visit(p->data);//访问根结点
			Push(&s, p); //将指针p的节点压入栈中
			p = p->Lchild; //遍历左子树
		}
		if (StackEmpty(&s) != 1) { //栈不为空
			p = Pop(&s); //根结点出栈,相当于回退
			p = p->rchild; //遍历右子树
		}
	}
	DestroyStack(&s);
}

2.中序遍历

中序遍历的顺序为:左子树->根结点>右子树

基本过程:

(1)将根结点入栈

(2)循环逐个访问左子树,执行(1)中步骤;当访问到没有左子树的结点时,跳出循环

(3)栈不为空,根结点出栈,访问根结点,再访问右子树

其实就是将访问根结点的位置换了

代码实现:

void MidOrder(BiNode *bt) { //树的中序遍历
	SqStack s;
	s = InitStack();
	BiNode *p = bt;
	while (p != NULL || StackEmpty(&s) != 1) { //当p为空,栈也为空时退出循环
		while (p != NULL) {
			Push(&s, p); //将指针p的节点压入栈中
			p = p->Lchild; //遍历左子树
		}
		if (StackEmpty(&s) != 1) { //栈不为空
			p = Pop(&s); //根结点出栈,相当于回退
			visit(p->data);//访问根结点
			p = p->rchild; //遍历右子树
		}
	}
	DestroyStack(&s);
}

3.后序遍历

 前两种遍历的栈数据类型都是BiNode *,但后序遍历的栈的数据类型要进行重新定义,因为后序遍历的顺序是左子树->右子树>根结点,结点要进入两次栈,出两次栈,为什么会有两次呢?

(1)第一次出栈:只遍历完左子树,该结点不能出栈,需要第二次入栈;找到右子树并遍历

(2)第二次出栈:遍历完左右子树,该结点出栈,并访问

需要注意的是,第二次出栈并访问之后,需要将p指针置空,这样才能在下一次循环的时候,重新从栈中取到一个元素(或者理解成二叉树中的回退操作)

这里设置一个flag标志来区分两次出入栈,并进行不同的操作

栈元素类型定义如下:

typedef struct element {
	BiNode *ptr;
	int flag;
} element;

代码实现:

(因为后序遍历和前两种数据类型不一样,这里定义了两套栈函数分别来用,用_1下标区分)

void PostOrder(BiNode *bt) { //树的后序遍历
	SqStack s;
	s = InitStack_1();
	BiNode *p = bt;
	element elem;
	while (p != NULL || StackEmpty_1(&s) != 1) { //当p为空,栈也为空时退出循环
		if (p != NULL) {//第一次入栈,访问左子树
			elem.ptr = p;
			elem.flag = 1; //标记flag为1,表示即将第一次入栈
			Push_1(&s, elem); //将指针p的结点第一次压入栈中
			p = p->Lchild;
		} else {
			elem = Pop_1(&s); //出栈
			p = elem.ptr; //p指向当前要处理的结点
			if (elem.flag == 1) {
				//flag==1时,说明只访问过左子树,还要访问右子树
				elem.flag = 2;
				Push_1(&s, elem); //结点第二次压入栈中
				p = p->rchild;
			} else {
				//flag==2时,左右子树都已经访问过了
				visit(p->data);
				p = NULL; //访问后,p赋为空,确保下次循环时继续出栈(相当于回退)
			}
		}
	}
	DestroyStack_1(&s);
}

4. 完整代码

分为三个文件包,一个是存放栈的操作函数,一个是存放二叉树的非递归遍历函数,一个是对二叉树的非递归遍历功能进行的测试,第三个文件调用前两个头文件就可以测试完整功能

(1)数组堆栈_二叉树非递归.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef char Datatype;

typedef struct BiNode {
	Datatype data;//数据内容
	struct BiNode  *Lchild;//指向左孩子结点
	struct BiNode  *rchild;//指向右孩子结点
} BiNode;

typedef BiNode *Elemtype;
typedef struct element {
	BiNode *ptr;
	int flag;
} element;

typedef element Elemtype_1;
typedef struct {
	Elemtype *data;//用于前序和中序遍历
	Elemtype_1 *data_1;//用于后序遍历
	int top;//栈顶指针,这里用int类型表示指针的下标
	int stacksize;
} SqStack;
Elemtype Pop(SqStack *s);

SqStack InitStack() {//空栈构造函数
	SqStack s;
	s.data = (Elemtype *)malloc(STACK_INIT_SIZE * sizeof(Elemtype));
	s.top = -1; //表示栈空
	s.stacksize = STACK_INIT_SIZE;
	if (s.data != NULL)
	{}
	else
		printf("Init error!\n");
	return s;
}

void DestroyStack(SqStack *s) {//销毁栈函数
	free(s->data);
}

int StackEmpty(SqStack *s) {//判断是否为空栈,是返回1,否 返回0
	if (s->top == -1)
		return 1;
	else
		return 0;
}

void Push(SqStack *s, Elemtype e) {//添加元素入栈
	if (s->top >= s->stacksize) {
		s->data = (Elemtype *)malloc((STACK_INIT_SIZE + STACKINCREMENT) * sizeof(Elemtype));
		s->stacksize += STACKINCREMENT;
		if (s->data != NULL) {}
		else
			printf("Push error!\n");
	} else {
		s->top++;
		s->data[s->top] = e;
	}
}

Elemtype Pop(SqStack *s) {
	if (StackEmpty(s) != 1 && s->top >= 0) {
		Elemtype e = s->data[s->top];
		s->top--;
		return e;
	}
	printf("Pop error!\n");
}

SqStack InitStack_1() {//空栈构造函数
	SqStack s;
	s.data_1 = (Elemtype_1 *)malloc(STACK_INIT_SIZE * sizeof(Elemtype_1));
	s.top = -1; //表示栈空
	s.stacksize = STACK_INIT_SIZE;
	if (s.data != NULL)
	{}
	else
		printf("Init error!\n");
	return s;
}

void DestroyStack_1(SqStack *s) {//销毁栈函数
	free(s->data_1);
}

int StackEmpty_1(SqStack *s) {//判断是否为空栈,是返回1,否 返回0
	if (s->top == -1)
		return 1;
	else
		return 0;
}

void Push_1(SqStack *s, Elemtype_1 e) {//添加元素入栈
	if (s->top >= s->stacksize) {
		s->data_1 = (Elemtype_1 *)malloc((STACK_INIT_SIZE + STACKINCREMENT) * sizeof(Elemtype_1));
		s->stacksize += STACKINCREMENT;
		if (s->data_1 != NULL) {}
		else
			printf("Push error!\n");
	} else {
		s->top++;
		s->data_1[s->top] = e;
	}
}

Elemtype_1 Pop_1(SqStack *s) {
	if (StackEmpty(s) != 1 && s->top >= 0) {
		Elemtype_1 e = s->data_1[s->top];
		s->top--;
		return e;
	}
	printf("Pop error!\n");
}

 (2)二叉树遍历(非递归).h

#include "数组堆栈_二叉树非递归.h"

BiNode *Creat(char *str, int *i, int len) { //树的创建
	struct BiNode *bt = NULL;
	char ch = str[(*i)++];
	if (ch == '#' || *i >= len) {
		bt = NULL;
	} else {
		bt = (struct BiNode *)malloc(sizeof(BiNode));
		if (bt != NULL) {
			bt->data = ch;
			bt->Lchild = Creat(str, i, len); //这里的递归要赋值,这样才能建立不同域中的连接关系
			bt->rchild = Creat(str, i, len);
		}
	}
	return bt;//返回的一直是根结点
}

void visit(Datatype e) {
	printf("%c ", e);
}

void PreOrder(BiNode *bt) { //树的前序遍历
	SqStack s;
	s = InitStack();
	BiNode *p = bt;
	while (p != NULL || StackEmpty(&s) != 1) { //当p为空,栈也为空时退出循环
		while (p != NULL) {
			visit(p->data);//访问根结点
			Push(&s, p); //将指针p的节点压入栈中
			p = p->Lchild; //遍历左子树
		}
		if (StackEmpty(&s) != 1) { //栈不为空
			p = Pop(&s); //根结点出栈,相当于回退
			p = p->rchild; //遍历右子树
		}
	}
	DestroyStack(&s);
}

void MidOrder(BiNode *bt) { //树的中序遍历
	SqStack s;
	s = InitStack();
	BiNode *p = bt;
	while (p != NULL || StackEmpty(&s) != 1) { //当p为空,栈也为空时退出循环
		while (p != NULL) {
			Push(&s, p); //将指针p的节点压入栈中
			p = p->Lchild; //遍历左子树
		}
		if (StackEmpty(&s) != 1) { //栈不为空
			p = Pop(&s); //根结点出栈,相当于回退
			visit(p->data);//访问根结点
			p = p->rchild; //遍历右子树
		}
	}
	DestroyStack(&s);
}

void PostOrder(BiNode *bt) { //树的后序遍历
	SqStack s;
	s = InitStack_1();
	BiNode *p = bt;
	element elem;
	while (p != NULL || StackEmpty_1(&s) != 1) { //当p为空,栈也为空时退出循环
		if (p != NULL) {//第一次入栈,访问左子树
			elem.ptr = p;
			elem.flag = 1; //标记flag为1,表示即将第一次入栈
			Push_1(&s, elem); //将指针p的结点第一次压入栈中
			p = p->Lchild;
		} else {
			elem = Pop_1(&s); //出栈
			p = elem.ptr; //p指向当前要处理的结点
			if (elem.flag == 1) {
				//flag==1时,说明只访问过左子树,还要访问右子树
				elem.flag = 2;
				Push_1(&s, elem); //结点第二次压入栈中
				p = p->rchild;
			} else {
				//flag==2时,左右子树都已经访问过了
				visit(p->data);
				p = NULL; //访问后,p赋为空,确保下次循环时继续出栈(相当于回退)
			}
		}
	}
	DestroyStack_1(&s);
}

 (3)二叉树遍历(非递归).c

#include "二叉树遍历(非递归).h"

main() {
	printf("测试二叉树遍历(非递归)算法\n");
	printf("建立一个二叉树-->");
	BiNode *bt;
	int i = 0, len;
	char str[50];
	printf("输入一个字符串用于建立二叉树:");
	scanf("%s", str);
	len = strlen(str);
	bt = Creat(str, &i, len);
	printf("测试遍历操作:\n");
	printf("测试树的前序遍历:");
	PreOrder(bt);
	printf("\n");
	printf("测试树的中序遍历:");
	MidOrder(bt);
	printf("\n");
	printf("测试树的后序遍历:");
	PostOrder(bt);
	printf("\n");
}

5.测试输出

二叉树遍历的非递归算法

 初学小白,有错误的话欢迎指正喔!~文章来源地址https://www.toymoban.com/news/detail-432969.html

到了这里,关于二叉树遍历的非递归算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 二叉树的遍历的递归与非递归算法

    按照一定规律对二叉树的 每个结点进行访问且 仅访问一次 ; 这里的访问:可以是计算二叉树中的结点数据,打印该结点的信息,也可以是对结点进行的任何其它操作! 为什么需要遍历二叉树? 从过遍历可以得到访问结点的顺序序列,遍历操作就是将二叉树的结点按一定的

    2024年04月15日
    浏览(36)
  • 二叉树的遍历(先序遍历,中序遍历,后序遍历)递归与非递归算法

    先序遍历:先遍历一颗树的根节点,后遍历左子树,最后遍历右子树     先序遍历序列: 1 - 2 - 4 - 5 - 3 - 6 - 7 分解子问题方法 思路:将一颗二叉树看做两个部分,一个部分是左路节点,另一个部分是左路节点的右子树,先将二叉树的左路节点全部入栈,再依次出栈,出栈的

    2024年02月14日
    浏览(40)
  • 【数据结构】二叉树的遍历递归算法详解

    我们来写一个函数 BuyNode(x)函数 用于创建二叉树结点。 用动态开辟函数 malloc 函数进行动态开辟,并强制转换为 BTNode 型,用变量 node 来去管理开辟的空间。 我们初始化结点,其 val 即为传入的参数x,左右指针 left 和 right 都设为NULL。 我们在主函数中创建上面这样一颗二叉树

    2024年01月20日
    浏览(46)
  • 二叉树中序遍历非递归算法C语言实现

    // //  Inordertraverse.c //  二叉树链式存储 // //  Created by 丘** on 2021/7/28. // #include \\\"Inordertraverse.h\\\" #include stdbool.h #includestdlib.h typedef   struct StackNode {     int data;     struct StackNode* next;      }StackNode; typedef struct BiTNode {     int data;     struct BiTNode* lchild,* rchild;           }BiTnode

    2024年02月02日
    浏览(39)
  • 【算法第十一天7.25】二叉树前、中、后递归、非递归遍历

    树的结构 ================================================ 链接 :力扣94-二叉树中序遍历 递归 思路 1、 确定返回值和方法参数 :需要集合来存放树各节点的值,最后打印出来,所以需要一个list集合作为参数,不断迭代;除此之外不需要有返回值 2、 确定终止条件 :当前节点为空时,

    2024年02月15日
    浏览(45)
  • 数据结构与算法----详解二叉树的遍历(迭代、递归)

    ❤️ 作者简介 :大家好我是小鱼干儿♛是一个热爱编程、热爱算法的大三学生,蓝桥杯国赛二等奖获得者 🐟 个人主页 :https://blog.csdn.net/qq_52007481 ⭐ 个人社区 :【小鱼干爱编程】 🔥 算法专栏 :算法竞赛进阶指南 💯 刷题网站 :虽然市面上有很多的刷题网站,但是里面

    2024年01月24日
    浏览(54)
  • 二叉树前中后序的非递归实现

    前言  :  递归我们会有一些问题的 为什么有递归就一定有非递归呢??首先递归是有一定缺陷的 递归真正的缺陷是,每一个程序运行起来呢都是一个线程的形式,但是每一个线程都会有独立的栈空间,但是栈空间是很小的,当递归的深度太深容易栈溢出!!        只要

    2024年02月13日
    浏览(47)
  • 二叉树的前,中,后序的非递归实现(c++)

    前言         对于二叉树来说,遍历它有多种方式,其中递归遍历是比较简单的,但是非递归的实现就有一定的难度,在这里介绍一种非递归实现二叉树遍历的方式。         其实对于二叉树的非递归实现,实际上就是用代码来 模拟操作系统压栈和弹栈的过程 。让我们一起

    2024年02月14日
    浏览(38)
  • 二叉树的前 中 后序的非递归实现(图文详解)

    🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨ 🐻强烈推荐优质专栏: 🍔🍟🌯C++的世界(持续更新中) 🐻推荐专栏1: 🍔🍟🌯C语言初阶 🐻推荐专栏2: 🍔🍟🌯C语言进阶 🔑个人信条: 🌵知行合一 🍉本篇简介::非递归实现二叉树的前中后序遍历. 金句分享: ✨不要慌,不要慌,太阳下了

    2024年02月08日
    浏览(37)
  • 算法练习第13天|递归实现 144.二叉树的前序遍历(opens new window)145.二叉树的后序遍历(opens new window)94.二叉树的中序遍历

    二叉树的存储方式:链式存储和顺序存储。链式存储用指针,顺序存储用数组。其结构如下图所示。 链式存储: 顺序存储: 顺序存储时,若父节点下表为i,则其左孩子下标为 2*i + 1,右孩子下标为2*i + 2. 二叉树主要有两种遍历方式: 深度优先遍历:先往深走,遇到叶子节点

    2024年02月22日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包