二叉树遍历C语言(递归,非递归)六种算法

合集下载

二叉树后序遍历的递归和非递归算法

二叉树后序遍历的递归和非递归算法

安 徽电气工 程 职 业 技术学 院学报
:薹6 M2 a r 0 c h 0

-X树后序遍历的递归和非递归算法
孙泽宇, 赵国增 , 舒云星・
( 洛阳工业高等专科学校计算机系 , 河南 洛阳 4 10 ) 703
[ 要 ] 论述了二又树后序遍历的递归算法和非递归算法, 摘 对递归算法中的工作栈 的执行过程做 了
Srcbt e t t ie { u r
● 收稿 日期 :0 5—1 0 70 . 2— 2
作者筒介: 孙泽字(97 . 吉林长春人. 17 一) 男。 洛阳工业高等专科学校计算机秉麓师。研究方向: 人工智能。 趟 目增 (97 . 河南越壁人 。 阳工业高等专科 学校计算机 秉麓师 。研究方 向: 1 一) 男。 7 洛 人工智能。
s c br 木e , r h;} t t ie lt 木 i t m te f g
后序遍历二叉树的递归算法如下 :
T p d fs u tBT o e y e e r c in d t
法及执行时栈 的变化情况 , 可设计 出较好 的非递归化算法 , 本文讨论了二叉树后序遍历的递归和非递归
算法。 2 后序遍历二叉树的递归算法
1 后序遍历左子树( ) 若左子树不为空 ) 2 后序遍历右子树( ) 若右子树不为空 ) 3 访问根结点 ( ) 若存在根结点)
二叉树数据结构如下 :
二叉树是数据结构 中最常见 的存储形式 , 在算法与数据结构中经常使用。树与森林都可以转换为 二叉树 , 而遍历算法则是二叉树最重要的操作 。所谓遍历二叉树 , 就是遵从某种次序 , 遍访二叉树 中的
所有结点, 使得每个结点被访问一次 , 而且仅一次。在遍历算法中, 递归算法是最普遍 的, 弄清 了递归算

后序遍历的非递归算法(C详细)

后序遍历的非递归算法(C详细)

后序遍历的非递归算法(C详细)后序遍历是二叉树遍历的一种方式,它的顺序是先遍历左子树,然后遍历右子树,最后访问根节点。

非递归实现后序遍历的算法可以使用栈来辅助实现。

首先,我们需要定义一个树节点的数据结构,例如:```cstruct TreeNodeint val;struct TreeNode* left;struct TreeNode* right;};```接下来,我们使用一个辅助栈来进行非递归后序遍历。

首先需要创建一个空栈,并将根节点入栈。

然后开始循环,直到栈为空为止。

在循环中,首先取出栈顶节点,如果该节点没有左子树且没有右子树,说明该节点是叶子节点,可以直接输出该节点的值。

如果该节点有左子树或者右子树,需要判断是否已经遍历过该节点的子节点。

为了实现后序遍历的顺序,我们需要一个标记变量来记录上次访问的节点。

如果上次访问的节点是该节点的右子树,说明该节点的左右子节点都已经访问过了,可以直接输出该节点的值。

反之,如果上次访问的节点不是该节点的右子树,将该节点重新入栈,并以右、左、中的顺序将其右子树、左子树入栈。

下面给出完整的代码实现:```c#include <stdio.h>#include <stdlib.h>struct TreeNodeint val;struct TreeNode* left;struct TreeNode* right;};void postOrderTraversal(struct TreeNode* root)if (root == NULL)return;}struct TreeNode* lastVisited = NULL; // 上次访问的节点struct TreeNode* node = root; // 当前遍历的节点struct TreeNode* stack[100]; // 栈int top = -1; // 栈顶指针while (node != NULL , top != -1)if (node != NULL)stack[++top] = node; // 入栈node = node->left; // 访问左子树} elsestruct TreeNode* temp = stack[top]; // 取出栈顶节点if (temp->right == NULL , temp->right == lastVisited) printf("%d ", temp->val);top--; // 出栈lastVisited = temp; // 记录上次访问的节点} elsenode = temp->right; // 访问右子树}}}struct TreeNode* createNode(int val)struct TreeNode* node = (structTreeNode*)malloc(sizeof(struct TreeNode));if (node != NULL)node->val = val;node->left = NULL;node->right = NULL;}return node;int mai//创建一个二叉树struct TreeNode* root = createNode(1); root->left = createNode(2);root->right = createNode(3);root->left->left = createNode(4);root->left->right = createNode(5); root->right->left = createNode(6); root->right->right = createNode(7);//后序遍历二叉树printf("后序遍历结果:"); postOrderTraversal(root);printf("\n");return 0;```以上代码中,我们使用了一个辅助数组作为栈来实现非递归遍历。

二叉树的遍历及常用算法

二叉树的遍历及常用算法

⼆叉树的遍历及常⽤算法⼆叉树的遍历及常⽤算法遍历的定义:按照某种次序访问⼆叉树上的所有结点,且每个节点仅被访问⼀次;遍历的重要性:当我们需要对⼀颗⼆叉树进⾏,插⼊,删除,查找等操作时,通常都需要先遍历⼆叉树,所有说:遍历是⼆叉树的基本操作;遍历思路:⼆叉树的数据结构是递归定义(每个节点都可能包含相同结构的⼦节点),所以遍历也可以使⽤递归,即结点不为空则继续递归调⽤每个节点都有三个域,数据与,左孩⼦指针和右孩⼦之指针,每次遍历只需要读取数据,递归左⼦树,递归右⼦树,这三个操作三种遍历次序:根据访问三个域的不同顺序,可以有多种不同的遍历次序,⽽通常对于⼦树的访问都按照从左往右的顺序;设:L为遍历左⼦树,D为访问根结点,R为遍历右⼦树,且L必须位于R的前⾯可以得出以下三种不同的遍历次序:先序遍历操作次序为DLR,⾸先访问根结点,其次遍历根的左⼦树,最后遍历根右⼦树,对每棵⼦树同样按这三步(先根、后左、再右)进⾏中序遍历操作次序为LDR,⾸先遍历根的左⼦树,其次访问根结点,最后遍历根右⼦树,对每棵⼦树同样按这三步(先左、后根、再右)进⾏后序遍历操作次序为LRD,⾸先遍历根的左⼦树,其次遍历根的右⼦树,最后访问根结点,对每棵⼦树同样按这三步(先左、后右、最后根)进⾏层次遍历层次遍历即按照从上到下从左到右的顺序依次遍历所有节点,实现层次遍历通常需要借助⼀个队列,将接下来要遍历的结点依次加⼊队列中;遍历的应⽤“遍历”是⼆叉树各种操作的基础,可以在遍历过程中对结点进⾏各种操作,如:对于⼀棵已知⼆叉树求⼆叉树中结点的个数求⼆叉树中叶⼦结点的个数;求⼆叉树中度为1的结点个数求⼆叉树中度为2的结点个数5求⼆叉树中⾮终端结点个数交换结点左右孩⼦判定结点所在层次等等...C语⾔实现:#include <stdio.h>//⼆叉链表数据结构定义typedef struct TNode {char data;struct TNode *lchild;struct TNode *rchild;} *BinTree, BinNode;//初始化//传⼊⼀个指针令指针指向NULLvoid initiate(BinTree *tree) {*tree = NULL;}//创建树void create(BinTree *BT) {printf("输⼊当前结点值: (0则创建空节点)\n");char data;scanf(" %c", &data);//连续输⼊整形和字符时.字符变量会接受到换⾏,所以加空格if (data == 48) {*BT = NULL;return;} else {//创建根结点//注意开辟的空间⼤⼩是结构体的⼤⼩⽽不是结构体指针⼤⼩,写错了不会⽴马产⽣问题,但是后续在其中存储数据时极有可能出现内存访问异常(飙泪....) *BT = malloc(sizeof(struct TNode));//数据域赋值(*BT)->data = data;printf("输⼊节点 %c 的左孩⼦ \n", data);create(&((*BT)->lchild));//递归创建左⼦树printf("输⼊节点 %c 的右孩⼦ \n", data);create(&((*BT)->rchild));//递归创建右⼦树}}//求双亲结点(⽗结点)BinNode *Parent(BinTree tree, char x) {if (tree == NULL)return NULL;else if ((tree->lchild != NULL && tree->lchild->data == x) || (tree->rchild != NULL && tree->rchild->data == x))return tree;else{BinNode *node1 = Parent(tree->lchild, x);BinNode *node2 = Parent(tree->rchild, x);return node1 != NULL ? node1 : node2;}}//先序遍历void PreOrder(BinTree tree) {if (tree) {//输出数据printf("%c ", tree->data);//不为空则按顺序继续递归判断该节点的两个⼦节点PreOrder(tree->lchild);PreOrder(tree->rchild);}}//中序void InOrder(BinTree tree) {if (tree) {InOrder(tree->lchild);printf("%c ", tree->data);InOrder(tree->rchild);}}//后序void PostOrder(BinTree tree) {if (tree) {PostOrder(tree->lchild);PostOrder(tree->rchild);printf("%c ", tree->data);}}//销毁结点递归free所有节点void DestroyTree(BinTree *tree) {if (*tree != NULL) {printf("free %c \n", (*tree)->data);if ((*tree)->lchild) {DestroyTree(&((*tree)->lchild));}if ((*tree)->rchild) {DestroyTree(&((*tree)->rchild));}free(*tree);*tree = NULL;}}// 查找元素为X的结点使⽤的是层次遍历BinNode *FindNode(BinTree tree, char x) {if (tree == NULL) {return NULL;}//队列BinNode *nodes[1000] = {};//队列头尾位置int front = 0, real = 0;//将根节点插⼊到队列尾nodes[real] = tree;real += 1;//若队列不为空则继续while (front != real) {//取出队列头结点输出数据BinNode *current = nodes[front];if (current->data == x) {return current;}front++;//若当前节点还有⼦(左/右)节点则将结点加⼊队列if (current->lchild != NULL) {nodes[real] = current->lchild;real++;}if (current->rchild != NULL) {nodes[real] = current->rchild;real++;}}return NULL;}//层次遍历// 查找元素为X的结点使⽤的是层次遍历void LevelOrder(BinTree tree) {if (tree == NULL) {return;}//队列BinNode *nodes[1000] = {};//队列头尾位置int front = 0, real = 0;//将根节点插⼊到队列尾nodes[real] = tree;real += 1;//若队列不为空则继续while (front != real) {//取出队列头结点输出数据BinNode *current = nodes[front];printf("%2c", current->data);front++;//若当前节点还有⼦(左/右)节点则将结点加⼊队列if (current->lchild != NULL) {nodes[real] = current->lchild;real++;}if (current->rchild != NULL) {nodes[real] = current->rchild;real++;}}}//查找x的左孩⼦BinNode *Lchild(BinTree tree, char x) {BinTree node = FindNode(tree, x);if (node != NULL) {return node->lchild;}return NULL;}//查找x的右孩⼦BinNode *Rchild(BinTree tree, char x) {BinTree node = FindNode(tree, x);if (node != NULL) {return node->rchild;}return NULL;}//求叶⼦结点数量int leafCount(BinTree *tree) {if (*tree == NULL)return 0;//若左右⼦树都为空则该节点为叶⼦,且后续不⽤接续递归了else if (!(*tree)->lchild && !(*tree)->rchild)return 1;else//若当前结点存在⼦树,则递归左右⼦树, 结果相加return leafCount(&((*tree)->lchild)) + leafCount(&((*tree)->rchild));}//求⾮叶⼦结点数量int NotLeafCount(BinTree *tree) {if (*tree == NULL)return 0;//若该结点左右⼦树均为空,则是叶⼦,且不⽤继续递归else if (!(*tree)->lchild && !(*tree)->rchild)return 0;else//若当前结点存在左右⼦树,则是⾮叶⼦结点(数量+1),在递归获取左右⼦树中的⾮叶⼦结点,结果相加 return NotLeafCount(&((*tree)->lchild)) + NotLeafCount(&((*tree)->rchild)) + 1;}//求树的⾼度(深度)int DepthCount(BinTree *tree) {if (*tree == NULL)return 0;else{//当前节点不为空则深度+1 在加上⼦树的⾼度,int lc = DepthCount(&((*tree)->lchild)) + 1;int rc = DepthCount(&((*tree)->rchild)) + 1;return lc > rc?lc:rc;// 取两⼦树深度的最⼤值 }}//删除左⼦树void RemoveLeft(BinNode *node){if (!node)return;if (node->lchild)DestroyTree(&(node->lchild));node->lchild = NULL;}//删除右⼦树void RemoveRight(BinNode *node){if (!node)return;if (node->rchild)DestroyTree(&(node->rchild));node->rchild = NULL;}int main() {BinTree tree;create(&tree);BinNode *node = Parent(tree, 'G');printf("G的⽗结点为%c\n",node->data);BinNode *node2 = Lchild(tree, 'D');printf("D的左孩⼦结点为%c\n",node2->data);BinNode *node3 = Rchild(tree, 'D');printf("D的右孩⼦结点为%c\n",node3->data);printf("先序遍历为:");PreOrder(tree);printf("\n");printf("中序遍历为:");InOrder(tree);printf("\n");printf("后序遍历为:");PostOrder(tree);printf("\n");printf("层次遍历为:");LevelOrder(tree);printf("\n");int a = leafCount(&tree);printf("叶⼦结点数为%d\n",a);int b = NotLeafCount(&tree);printf("⾮叶⼦结点数为%d\n",b);int c = DepthCount(&tree);printf("深度为%d\n",c);//查找F节点BinNode *node4 = FindNode(tree,'C');RemoveLeft(node4);printf("删除C的左孩⼦后遍历:");LevelOrder(tree);printf("\n");RemoveRight(node4);printf("删除C的右孩⼦后遍历:");LevelOrder(tree);printf("\n");//销毁树printf("销毁树 \n");DestroyTree(&tree);printf("销毁后后遍历:");LevelOrder(tree);printf("\n");printf("Hello, World!\n");return 0;}测试:测试数据为下列⼆叉树:运⾏程序复制粘贴下列内容:ABDGHECKFIJ特别感谢:iammomo。

遍历二叉树的非递归算法

遍历二叉树的非递归算法

问一次。这里的“ 问”的含义很广 ,比如修 改或输出结点的信息, 访 删除结 我们知道 , 二叉树有三个基本的组成部分, 根, 即: 左子树和右予 树, 只 要依次遍历这三个 部分, 能遍历整个二叉树 。 遍历二叉树的方式通常有 就
算, 所用到的数据仅为整型或实型即能满足要求 , 计算求精课程称作数值方 点等等。
子树, 再访问右子树 , 最后访 问根结 点) 。由于二叉树定义 的递归性, 我们很 容易就会想到用递 归算法来遍历二叉树。 设二叉树与栈 的结构如下 ( c 用 语言描述) :
t p d fs r c i N d y e e tu t B T o e f
c a d t h r a a:
据结构会对应复杂程度不 同的算法 ,丽设计一个合适 的数据 结构 能使算法 三 种, 先序遍历 ( 即: 先访 问根 结点, 再访问左子树 , 最后访问右子树) 中序 、 先访问左 予树 , 再访 问根结点, 后访 问右子树) 后序遍历 ( 最 。 先访问左 的复杂程度大大降低。 编程人员在实践 中体会到 ; 学好~种高级语言仪能解 遍历 ( 决三成所遇到的 问题, 而学好数据结构却 能解 决八成所遇 到的问题, 因此, 在软件 设计中选择一个合适的数据结构越发显得重要 。 在 管理科学领域中, 很多问题都可 以转化 为树 T e r e型结构 , 而多叉树
就会不同。
)A r ys q e c [a ] ra , eu n eM x
t p d f tu t y e e s r c
树, 它有 4 个结点。为了便于理解遍历思想 , 暂时为每个没有 予树 的结点都

e ely e b s 1 Ⅱ p 赤 a e: t e e t p *t p' lmye o ,

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

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

右子 树还未访 问)( 序访 问 A的左子树 的根结点 B , 和 l ,先 )B 进 栈 . 于 B的左 子 树 为 空 , 以 B 和 1出 栈 ,中 序 访 问 B)B 由 所 ( , 和 O进栈 ( O表 示 开 始 遍 历 结 点 B 的 右 子 树 ) 由 于 B 的 右 子树 . 为空 。 B和 O出栈 ,后 序访 问 B . 和 1出 栈 。 中序 访 问 A)A ( )A ( , 和 O进栈 .先 序 访 A的 右 子 树 的 根 结 点 C , ( )C和 1进 栈 , 由 于 C的左子树为空 , C和 l出栈 .中序 访问 C 。 ( )C和 O进栈 , 由 于 C 的 右 子 树 为 空 。 和 0出 栈 . 后 序 访 问 C)A 和 O出 栈 , C ( . ( 序 访 问 A)此 时 栈 已 空 , 历 过 程 结 束 。 后 , 遍 从 上 面可 知 , 每个 结 点 进栈 、 出栈 都 是 两 次 。若 进 栈 前 访 问 该结点 , 则得 到先 序 序 列 A C; 在 第 一 次 出栈 时 济 问 该结 点 , B 若 则得 到 中序 序 列 B C: 在 第 二 次 出栈 时访 问 该 结 点 , A 若 则得 到 后 序 序 列 B A。 此 . C 因 只需 对 二 叉树 遍 历 一 次 即 可 得 到 三 种 遍 历序 列 这里的关键是设置了一个标志位 . 用来 说明该结点的右子树 是 否 已访 问 . 以此 表 示 该 结 点 是第 一 次 出栈 还 是 第 二 次 出栈 。
维普资讯
20 0 6年 第 6期

建 电

11 2
二叉树遍历的通用非递归算 法
徐凤生 1 李立群 2 马夕荣 2
( . 州 学 院 计算 机 系 。 东 德 州 2 32 2 山 东省 农 业 管 理 干部 学 院 , 东 济 南 2 0 0 ) 1德 山 503 . 山 5 10

二叉树的遍历算法实验报告

二叉树的遍历算法实验报告

二叉树的遍历算法实验报告二叉树的遍历算法实验报告引言:二叉树是计算机科学中常用的数据结构之一,它是由节点组成的层次结构,每个节点最多有两个子节点。

在实际应用中,对二叉树进行遍历是一项重要的操作,可以帮助我们理解树的结构和节点之间的关系。

本文将介绍二叉树的三种遍历算法:前序遍历、中序遍历和后序遍历,并通过实验验证其正确性和效率。

一、前序遍历前序遍历是指先访问根节点,然后按照先左后右的顺序遍历左右子树。

具体的实现可以通过递归或者使用栈来实现。

我们以递归方式实现前序遍历算法,并进行实验验证。

实验步骤:1. 创建一个二叉树,并手动构造一些节点和它们之间的关系。

2. 实现前序遍历算法的递归函数,函数的输入为根节点。

3. 在递归函数中,首先访问当前节点,然后递归调用函数遍历左子树,最后递归调用函数遍历右子树。

4. 调用前序遍历函数,输出遍历结果。

实验结果:经过实验,我们得到了正确的前序遍历结果。

这证明了前序遍历算法的正确性。

二、中序遍历中序遍历是指按照先左后根再右的顺序遍历二叉树。

同样,我们可以使用递归或者栈来实现中序遍历算法。

在本实验中,我们选择使用递归方式来实现。

实验步骤:1. 继续使用前面创建的二叉树。

2. 实现中序遍历算法的递归函数,函数的输入为根节点。

3. 在递归函数中,首先递归调用函数遍历左子树,然后访问当前节点,最后递归调用函数遍历右子树。

4. 调用中序遍历函数,输出遍历结果。

实验结果:通过实验,我们得到了正确的中序遍历结果。

这证明了中序遍历算法的正确性。

三、后序遍历后序遍历是指按照先左后右再根的顺序遍历二叉树。

同样,我们可以使用递归或者栈来实现后序遍历算法。

在本实验中,我们选择使用递归方式来实现。

实验步骤:1. 继续使用前面创建的二叉树。

2. 实现后序遍历算法的递归函数,函数的输入为根节点。

3. 在递归函数中,首先递归调用函数遍历左子树,然后递归调用函数遍历右子树,最后访问当前节点。

4. 调用后序遍历函数,输出遍历结果。

c语言二叉树的先序,中序,后序遍历

c语言二叉树的先序,中序,后序遍历

c语言二叉树的先序,中序,后序遍历1、先序遍历先序遍历可以想象为,一个小人从一棵二叉树根节点为起点,沿着二叉树外沿,逆时针走一圈回到根节点,路上遇到的元素顺序,就是先序遍历的结果先序遍历结果为:A B D H I E J C F K G2、中序遍历中序遍历可以看成,二叉树每个节点,垂直方向投影下来(可以理解为每个节点从最左边开始垂直掉到地上),然后从左往右数,得出的结果便是中序遍历的结果中遍历结果为:H D I B E J A F K C G3、后序遍历后序遍历就像是剪葡萄,我们要把一串葡萄剪成一颗一颗的。

还记得我上面提到先序遍历绕圈的路线么?(不记得翻上面理解)就是围着树的外围绕一圈,如果发现一剪刀就能剪下的葡萄(必须是一颗葡萄)(也就是葡萄要一个一个掉下来,不能一口气掉超过1个这样),就把它剪下来,组成的就是后序遍历了。

后序遍历中,根节点默认最后面后序遍历结果:H I D J E B K F G C A4、口诀先序遍历:先根再左再右中序遍历:先左再根再右后序遍历:先左再右再根这里的根,指的是每个分叉子树(左右子树的根节点)根节点,并不只是最开始头顶的根节点,需要灵活思考理解5、代码展示#include<stdio.h>#include<stdlib.h>typedef struct Tree{int data; // 存放数据域struct Tree *lchild; // 遍历左子树指针struct Tree *rchild; // 遍历右子树指针}Tree,*BitTree;BitTree CreateLink(){int data;int temp;BitTree T;scanf("%d",&data); // 输入数据temp=getchar(); // 吸收空格if(data == -1){ // 输入-1 代表此节点下子树不存数据,也就是不继续递归创建return NULL;}else{T = (BitTree)malloc(sizeof(Tree)); // 分配内存空间T->data = data; // 把当前输入的数据存入当前节点指针的数据域中printf("请输入%d的左子树: ",data);T->lchild = CreateLink(); // 开始递归创建左子树printf("请输入%d的右子树: ",data);T->rchild = CreateLink(); // 开始到上一级节点的右边递归创建左右子树return T; // 返回根节点}}// 先序遍历void ShowXianXu(BitTree T) // 先序遍历二叉树{if(T==NULL) //递归中遇到NULL,返回上一层节点{return;}printf("%d ",T->data);ShowXianXu(T->lchild); // 递归遍历左子树ShowXianXu(T->rchild); // 递归遍历右子树}// 中序遍历void ShowZhongXu(BitTree T) // 先序遍历二叉树{if(T==NULL) //递归中遇到NULL,返回上一层节点{return;}ShowZhongXu(T->lchild); // 递归遍历左子树printf("%d ",T->data);ShowZhongXu(T->rchild); // 递归遍历右子树}// 后序遍历void ShowHouXu(BitTree T) // 后序遍历二叉树{if(T==NULL) //递归中遇到NULL,返回上一层节点{return;}ShowHouXu(T->lchild); // 递归遍历左子树ShowHouXu(T->rchild); // 递归遍历右子树printf("%d ",T->data);}int main(){BitTree S;printf("请输入第一个节点的数据:\n");S = CreateLink(); // 接受创建二叉树完成的根节点printf("先序遍历结果: \n");ShowXianXu(S); // 先序遍历二叉树printf("\n中序遍历结果: \n");ShowZhongXu(S); // 中序遍历二叉树printf("\n后序遍历结果: \n");ShowHouXu(S); // 后序遍历二叉树return 0;}。

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

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

二叉树后序遍历的非递归算法
二叉树后序遍历是指按照左子树、右子树、根节点的顺序遍历二叉树的过程。

与前序遍历和中序遍历不同,后序遍历需要考虑根节点的位置,因此需要使用栈来存储节点信息。

非递归算法一般使用栈来实现,因为后序遍历的过程中需要先遍历左子树和右子树,最后才遍历根节点,所以存储节点信息的栈需要进行一些特殊处理。

下面是二叉树后序遍历的非递归算法:
1. 创建一个空栈,并将根节点入栈。

2. 创建一个辅助变量pre表示上一个被遍历的节点。

3. 当栈不为空时,取出栈顶元素top,判断它是否为叶子节点或者它的左右子节点都被遍历过了(被遍历过的节点可以通过辅助变量pre来判断)。

4. 如果top为叶子节点或者它的左右子节点都被遍历过了,则将top出栈,并将它的值输出。

5. 如果不满足条件3,判断top的右子节点是否为pre,如果是,则说明右子树已经遍历完了,此时可以直接输出top的值,并将top出栈;如果不是,则将top的右子节点入栈。

6. 将top的左子节点入栈。

7. 将上一个被遍历的节点pre更新为top。

根据这个算法,我们可以分别对左子树和右子树进行遍历,并保证根节点最后被遍历到,从而实现二叉树的后序遍历。

这个算法的时间复杂度为O(n),空间复杂度为O(n)。

总的来说,二叉树的后序遍历是一种比较复杂的遍历方式,需要使用栈保存节点信息,并且需要特殊处理根节点的位置。

使用非递归算法实现后序遍历可以优化空间复杂度和避免栈溢出的问题。

二叉树的各种遍历算法及其深度算法

二叉树的各种遍历算法及其深度算法

二叉树的各种遍历算法及其深度算法一、二叉树的遍历算法二叉树是一种常见的数据结构,遍历二叉树可以按照根节点的访问顺序将二叉树的结点访问一次且仅访问一次。

根据遍历的顺序不同,二叉树的遍历算法可以分为三种:前序遍历、中序遍历和后序遍历。

1. 前序遍历(Pre-order Traversal):首先访问根节点,然后遍历左子树,最后遍历右子树。

可以用递归或者栈来实现。

2. 中序遍历(In-order Traversal):首先遍历左子树,然后访问根节点,最后遍历右子树。

可以用递归或者栈来实现。

3. 后序遍历(Post-order Traversal):首先遍历左子树,然后遍历右子树,最后访问根节点。

可以用递归或者栈来实现。

二、二叉树的深度算法二叉树的深度,也叫做高度,指的是从根节点到叶子节点的最长路径上的节点数目。

可以使用递归或者层次遍历的方式来计算二叉树的深度。

1.递归算法:二叉树的深度等于左子树的深度和右子树的深度的较大值加一、递归的终止条件是当节点为空时,深度为0。

递归的过程中通过不断递归左子树和右子树,可以求出二叉树的深度。

2.层次遍历算法:层次遍历二叉树时,每遍历完一层节点,深度加一、使用一个队列来辅助层次遍历,先将根节点加入队列,然后依次取出队列中的节点,将其左右子节点加入队列,直到队列为空,完成层次遍历。

三、示例为了更好地理解二叉树的遍历和求深度的算法,我们以一个简单的二叉树为例进行说明。

假设该二叉树的结构如下:A/\BC/\/\DEFG其中,A、B、C、D、E、F、G分别代表二叉树的结点。

1.前序遍历:A->B->D->E->C->F->G2.中序遍历:D->B->E->A->F->C->G3.后序遍历:D->E->B->F->G->C->A4.深度:2以上是针对这个二叉树的遍历和深度的计算示例。

二叉树二叉链表的层序遍历(C语言)

二叉树二叉链表的层序遍历(C语言)

⼆叉树⼆叉链表的层序遍历(C语⾔)所谓⼆叉树层序遍历,即从⼆叉树根结点开始,按从上到下、从左到右的顺序访问每⼀个结点。

每个结点只访问⼀次。

#include <stdio.h>#include <stdlib.h>/*** ⼆叉树⼆叉链表之⾮递归遍历:层序遍历* 算法思想:借助⼀个队列;根树进队;队不为空时循环“从队列中出⼀个树p,访问该树根结点;* 若它有左⼦树,左⼦树进队;若它有右⼦树,右⼦树进队。

”* (保证了元素进队顺序是从树的上层到下层,且同层元素在队列中从左到右相邻)* (注:为了节约空间,这⾥的树进队,是指树的地址进队,⽽不是树结点进队,队列中存放的是对各树地址的引⽤)*/#define OK 1;#define TURE 1;#define FALSE 0;#define ERROR 0;const int OVERFLOW = -2;const int MAXSIZE = 100;typedef int Status;typedef char TElemType;//⼆叉链表结构定义typedef struct BiNode{TElemType data;struct BiNode *lchild, *rchild;} BiNode, *BiTree;//顺序循环队列定义typedef struct {BiTree *base; //存放树型指针int front; //头指针,若队列不为空,指向队列头元素int rear; //尾指针,若队列不为空,指向队列尾元素的下⼀个位置} SqQueue;//由字符序列创建⼆叉树Status CreateBiTree(BiTree *tree,TElemType data[],int *j,int len){if((*j)<=len-1){if(data[(*j)]=='#'){(*tree)=NULL;(*j)++;} else {(*tree)=(BiTree)malloc(sizeof(BiNode));if(!(*tree)) return OVERFLOW;(*tree)->data=data[(*j)]; //⽣成根结点(*j)++;CreateBiTree(&((*tree)->lchild),data,j,len); //构造左⼦树CreateBiTree(&((*tree)->rchild),data,j,len); //构造右⼦树}}return OK;}//访问⼆叉树结点Status Visit(BiTree tree){printf("%c",tree->data);return OK;}//借助队列实现⼆叉链表的层序遍历Status LevelOrder_ByQueue(BiTree tree) {BiTree p;SqQueue queue1;InitQueue(&queue1);EnQueue(&queue1,tree); //根结点⼊队while(queue1.front!=queue1.rear){ //队不为空DeQueue(&queue1,&p); //根节点出队Visit(p);if(p->lchild!=NULL) EnQueue(&queue1,p->lchild); //有左孩⼦就进队if(p->rchild!=NULL) EnQueue(&queue1,p->rchild); //有右孩⼦也进队}return OK;}/***⽤到队列的相关函数***///循环队列初始化Status InitQueue(SqQueue *queue) {queue->base=(BiTree*)malloc(sizeof(BiTree)*MAXSIZE); //分配队列数组空间if(!queue->base) return OVERFLOW; //分配失败queue->front=0;queue->rear=0;return OK;}//循环队列⼊队操作Status EnQueue(SqQueue *queue,BiTree elem){if((queue->rear+1)%MAXSIZE==queue->front){ //队满return ERROR;} else {queue->base[queue->rear]=elem;queue->rear=(queue->rear+1)%MAXSIZE;return OK;}}//循环队列出队操作Status DeQueue(SqQueue *queue,BiTree *elem){if(queue->front==queue->rear){ //队空return ERROR;} else {(*elem)=queue->base[queue->front];queue->front=(queue->front+1)%MAXSIZE;return OK;}}int main(void){//⽰例⼆叉树的结构/*A/B/ \C D/ \E F\G*///指向⼆叉树的指针BiTree bitree1;//创建⼆叉树待⽤数据TElemType data1[]={'A','B','C','#','#','D','E','#','G','#','#','F','#','#','#',}; //先序遍历序列int len1=sizeof(data1)/sizeof(data1[0]);int* j1=(int*)malloc(sizeof(int));*j1=0;//按先序遍历序列创建⼆叉树Status createBiTreeResult = CreateBiTree(&bitree1,data1,j1,len1);printf("⼆叉树创建结果:%d\n",createBiTreeResult);//层序遍历⼆叉树Status levelOrder_ByQueue = LevelOrder_ByQueue(bitree1); //注:这⾥参数是⼆叉树根结点,⽽不是指针 printf("\n层序遍历⼆叉树结果:%d\n",levelOrder_ByQueue);printf("\nEND");return0;}。

非递归中序遍历二叉树课件

非递归中序遍历二叉树课件
由于在非递归实现中,我们使用栈来 模拟递归的过程,因此遍历后的结果 与递归实现相同。
04 非递归中序遍历 二叉树的复杂度 分析
时间复杂度
最好情况:O(n) 最坏情况:O(n)
平均情况:O(n)
空间复杂度
最好情况:O(1) 最坏情况:O(n)
平均情况:O(n)
05 非递归中序遍历 二叉树的优缺点
优点
01
02
03
空间效率高
非递归算法通常只需要常 数级别的额外空间,相比 之下,递归算法可能需要 更多的堆栈空间。
代码简洁
非递归算法的代码通常更 简洁,更易于理解和维护。
适合处理大型数据
由于非递归算法不需要大 量的堆栈空间,因此更适 合处理大型数据集。
缺点
编程技巧要求高
非递归算法需要更多的编程技巧, 特别是对于那些不熟悉这种技术 的人来说,理解和实现可能会比 较困难。
遍历过程
01
02
03
04
弹出栈顶元素,访问该 节点。
如果该节点右子节点存 在,将右子节点入栈。
如果该节点左子节点存 在,将左子节点入栈。
重复上述步骤,直到栈 为空。
遍历后的结果
01
中序遍历的顺序为:左子树 -> 根节点 -> 右子树。
02
非递归方法利用了栈的性质,实 现了从上到下、从左到右的遍历 顺序。
THANKS
感谢观看
栈为空。
实例二:复杂的二叉树
总结词:进阶应用
详细描述:对于复杂的二叉树,非递归中序遍历需要 更加细致的处理。由于树的形状可能不规则,我们需 要更加灵活地使用栈来处理节点之间的关系。在遍历 过程中,我们需要注意处理各种特殊情况,例如循环 引用、节点值相等的情况,以避免陷入无限循环或访 问错误的节点。此外,我们还需要注意优化算法的时 间复杂度和空间复杂度,以提高遍历的效率和准确性。

C语言实现二叉树的前序遍历

C语言实现二叉树的前序遍历

C语言实现二叉树的前序遍历二叉树是一种非线性数据结构,由节点和边组成。

每个节点最多有两个子节点,分别称为左子节点和右子节点。

二叉树可以用递归或迭代的方法进行前序、中序和后序遍历。

在本文中,我们将重点介绍如何使用递归方法实现二叉树的前序遍历。

前序遍历是指首先访问根节点,然后按照左子树->右子树的顺序遍历二叉树。

在实际编程中,我们可以通过递归的方式来遍历每个节点。

首先,让我们定义二叉树的节点结构。

```c//定义二叉树节点结构struct TreeNodeint val; // 节点值struct TreeNode* left; // 左子节点指针struct TreeNode* right; // 右子节点指针};```接下来,让我们实现二叉树的前序遍历函数。

```c//二叉树的前序遍历函数void preorderTraversal(struct TreeNode* root)if (root == NULL) { // 如果根节点为空,则返回return;}//首先打印根节点的值printf("%d ", root->val);//然后递归遍历左子树preorderTraversal(root->left);//最后递归遍历右子树preorderTraversal(root->right);```首先,我们判断根节点是否为空。

如果为空,表示已经遍历到叶子节点,直接返回。

然后,我们打印当前节点的值。

接下来,我们递归调用前序遍历函数,遍历左子树和右子树。

接下来,我们可以通过构建一个简单的二叉树来测试我们的前序遍历函数。

```c//创建一个二叉树用于测试前序遍历struct TreeNode* createTestTrestruct TreeNode* root = (structTreeNode*)malloc(sizeof(struct TreeNode)); // 创建根节点root->val = 1;root->left = (struct TreeNode*)malloc(sizeof(struct TreeNode)); // 创建左子节点root->left->val = 2;root->left->left = NULL;root->left->right = NULL;root->right = (struct TreeNode*)malloc(sizeof(struct TreeNode)); // 创建右子节点root->right->val = 3;root->right->left = NULL;root->right->right = NULL;return root;```在主函数中,我们创建一个测试二叉树,并调用前序遍历函数进行遍历。

二叉树的遍历及相关题目

二叉树的遍历及相关题目

⼆叉树的遍历及相关题⽬⼆叉树的遍历及相关题⽬1.1⼆叉树遍历的概念⼆叉树结构体的定义:typedef struct node{ ElemType data; struct node * lchild; struct node * rchild;}⼆叉树的遍历是指按照⼀定的次序访问⼆叉树中的所有的节点,并且每个节点仅访问⼀次的过程。

若规定先遍历左⼦树,后遍历右⼦树,则对于⾮空⼆叉树,可得到如下3种递归的遍历⽅法:(1)先序遍历访问根节点,先序遍历左⼦树,先序遍历右⼦树。

(根,左,右)(2)中序遍历中序遍历左⼦树,访问根节点,中序遍历右⼦树。

(左,根,右)(3)后序遍历后序遍历左⼦树,后序遍历右⼦树,访问根节点。

(左,右,根)除此之外也有层次遍历。

先访问根节点,在从左到右访问第⼆层的所有节点,从左到右访问第三层的所有节点......1.2⼆叉树遍历递归算法先序遍历递归算法:void PreOrder(BTNode * b){ if(n != NULL) { cout<<b->data; PreOrder(b->lchild); PreOrder(b->rchild); }}中序遍历递归算法void InOrder(BTNode * b){ if(n != NULL) { InOrder(b->lchild); cout<<b->data; InOrder(b->rchild); }}后序遍历递归算法:void PostOrder(BTNode * b){ if(b != NULL) { PostOrder(b->lchild); PostOrder(b->rchild); cout<<b->data; }}题⽬1:输出⼀个给定⼆叉树的所有的叶⼦节点:void DispLeaf(BTNode * b){ if(b != NULL) { if(b->lchild == NULL && b->rchild == NULL) cout<<b->data; DispLeaf(b->lchild); DispLeaf(b->rchild); }}以上算法采⽤先序遍历输出了所有的叶⼦节点,所以叶⼦节点是从左到右输出的。

二叉树各种计算公式总结

二叉树各种计算公式总结

二叉树各种计算公式总结二叉树是一种常见的数据结构,它由一个根节点和最多两个子节点组成。

许多计算问题可以通过对二叉树进行各种操作和遍历来解决。

在本文中,将总结二叉树的各种计算公式。

1.二叉树节点个数:二叉树节点个数的计算公式是N=N1+N2+1,其中N表示二叉树的节点个数,N1表示左子树的节点个数,N2表示右子树的节点个数。

2. 二叉树的高度:二叉树的高度是指从根节点到最远叶子节点的最长路径上的节点数量。

计算二叉树的高度的公式是H = max(H1, H2) + 1,其中H表示二叉树的高度,H1表示左子树的高度,H2表示右子树的高度。

3.二叉树的深度:二叉树的深度是指从根节点到当前节点的路径的长度。

计算二叉树的深度的公式是D=D1+1,其中D表示二叉树的深度,D1表示父节点的深度。

4.二叉查找树:二叉查找树是一种有序二叉树,它要求对于树中的每个节点,左子树的值都小于节点的值,右子树的值都大于节点的值。

在二叉查找树中进行的公式是:-如果目标值等于当前节点的值,则返回当前节点;-如果目标值小于当前节点的值,则在左子树中继续;-如果目标值大于当前节点的值,则在右子树中继续。

5.二叉树的遍历:二叉树的遍历是指按照一定的顺序访问二叉树中的所有节点。

常见的二叉树遍历方式有三种:- 前序遍历:先访问根节点,然后递归地访问左子树,最后递归地访问右子树。

可以表示为:root -> 左子树 -> 右子树。

- 中序遍历:先递归地访问左子树,然后访问根节点,最后递归地访问右子树。

可以表示为:左子树 -> root -> 右子树。

- 后序遍历:先递归地访问左子树,然后递归地访问右子树,最后访问根节点。

可以表示为:左子树 -> 右子树 -> root。

6.二叉树的最大路径和:二叉树的最大路径和是指二叉树中两个节点之间路径上的节点值的最大和。

可以通过递归地计算每个子树的最大路径和,然后选择最大的子树路径和来得出最终结果。

二叉树的各种算法

二叉树的各种算法

二叉树的各种算法1.二叉树的前序遍历算法:前序遍历是指先访问根节点,再访问左子树,最后访问右子树的遍历顺序。

具体算法如下:-如果二叉树为空,则直接返回。

-访问根节点,并输出或进行其他操作。

-递归地前序遍历左子树。

-递归地前序遍历右子树。

2.二叉树的中序遍历算法:中序遍历是指先访问左子树,再访问根节点,最后访问右子树的遍历顺序。

具体算法如下:-如果二叉树为空,则直接返回。

-递归地中序遍历左子树。

-访问根节点,并输出或进行其他操作。

-递归地中序遍历右子树。

3.二叉树的后序遍历算法:后序遍历是指先访问左子树,再访问右子树,最后访问根节点的遍历顺序。

具体算法如下:-如果二叉树为空,则直接返回。

-递归地后序遍历左子树。

-递归地后序遍历右子树。

-访问根节点,并输出或进行其他操作。

4.二叉树的层序遍历算法:层序遍历是按照从上到下、从左到右的顺序逐层遍历二叉树的节点。

具体算法如下:-如果二叉树为空,则直接返回。

-创建一个队列,将根节点入队。

-循环执行以下步骤,直到队列为空:-出队并访问当前节点,并输出或进行其他操作。

-若当前节点的左子节点不为空,则将左子节点入队。

-若当前节点的右子节点不为空,则将右子节点入队。

5.二叉树的深度算法:二叉树的深度是指从根节点到叶节点的最长路径的节点数。

具体算法如下:-如果二叉树为空,则深度为0。

-否则,递归地计算左子树的深度和右子树的深度,然后取较大的值加上根节点的深度作为二叉树的深度。

6.二叉树的查找算法:二叉树的查找可以使用前序、中序或后序遍历来完成。

具体算法如下:-如果二叉树为空,则返回空。

-如果当前节点的值等于目标值,则返回当前节点。

-否则,先在左子树中递归查找,如果找到则返回找到的节点。

-如果左子树中未找到,则在右子树中递归查找,如果找到则返回找到的节点。

-如果左右子树中都未找到,则返回空。

7.二叉树的插入算法:二叉树的插入可以使用递归或循环来实现。

具体算法如下:-如果二叉树为空,则创建一个新节点作为根节点,并返回根节点。

二叉树的遍历实验报告

二叉树的遍历实验报告

二叉树的遍历实验报告一、实验目的1.了解二叉树的基本概念和性质;2.理解二叉树的遍历方式以及它们的实现方法;3.学会通过递归和非递归算法实现二叉树的遍历。

二、实验内容1.二叉树的定义在计算机科学中,二叉树是一种重要的数据结构,由节点及它们的左右儿子组成。

没有任何子节点的节点称为叶子节点,有一个子节点的节点称为一度点,有两个子节点的节点称为二度点。

二叉树的性质:1.每个节点最多有两个子节点;2.左右子节点的顺序不能颠倒,左边是父节点的左子节点,右边是父节点的右子节点;3.二叉树可以为空,也可以只有一个根节点;4.二叉树的高度是从根节点到最深叶子节点的层数;5.二叉树的深度是从最深叶子节点到根节点的层数;6.一个深度为d的二叉树最多有2^(d+1) -1个节点,其中d>=1;7.在二叉树的第i层上最多有2^(i-1)个节点,其中i>=1。

2.二叉树的遍历方式二叉树的遍历是指从根节点出发,按照一定的顺序遍历二叉树中的每个节点。

常用的二叉树遍历方式有三种:前序遍历、中序遍历和后序遍历。

前序遍历:先遍历根节点,再遍历左子树,最后遍历右子树;中序遍历:先遍历左子树,再遍历根节点,最后遍历右子树;后序遍历:先遍历左子树,再遍历右子树,最后遍历根节点。

递归算法:利用函数调用,递归实现二叉树的遍历;非递归算法:利用栈或队列,对二叉树进行遍历。

三、实验步骤1.创建二叉树数据结构并插入节点;2.实现二叉树的前序遍历、中序遍历、后序遍历递归算法;3.实现二叉树的前序遍历、中序遍历、后序遍历非递归算法;4.测试算法功能。

四、实验结果1.创建二叉树数据结构并插入节点为了测试三种遍历方式的算法实现,我们需要创建一个二叉树并插入节点,代码如下:```c++//定义二叉树节点struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(NULL), right(NULL) {}};递归算法是实现二叉树遍历的最简单方法,代码如下:```c++//前序遍历非递归算法vector<int> preorderTraversal(TreeNode* root) {stack<TreeNode*> s;vector<int> res;if (!root) return res;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;}4.测试算法功能return 0;}```测试结果如下:preorderTraversal: 4 2 1 3 6 5 7inorderTraversal: 1 2 3 4 5 6 7postorderTraversal: 1 3 2 5 7 6 4preorderTraversalNonRecursive: 4 2 1 3 6 5 7inorderTraversalNonRecursive: 1 2 3 4 5 6 7postorderTraversalNonRecursive: 1 3 2 5 7 6 4本次实验通过实现二叉树的递归和非递归遍历算法,加深了对二叉树的理解,并熟悉了遍历算法的实现方法。

二叉树的四种遍历算法

二叉树的四种遍历算法

⼆叉树的四种遍历算法⼆叉树作为⼀种重要的数据结构,它的很多算法的思想在很多地⽅都⽤到了,⽐如STL算法模板,⾥⾯的优先队列、集合等等都⽤到了⼆叉树⾥⾯的思想,先从⼆叉树的遍历开始:看⼆叉树长什么样⼦:我们可以看到这颗⼆叉树⼀共有七个节点0号节点是根节点1号节点和2号节点是0号节点的⼦节点,1号节点为0号节点的左⼦节点,2号节点为0号节点的右⼦节点同时1号节点和2号节点⼜是3号节点、四号节点和五号节点、6号节点的双亲节点五号节点和6号节点没有⼦节点(⼦树),那么他们被称为‘叶⼦节点’这就是⼀些基本的概念⼆叉树的遍历⼆叉树常⽤的遍历⽅式有:前序遍历、中序遍历、后序遍历、层序遍历四种遍历⽅式,不同的遍历算法,其思想略有不同,我们来看⼀下这四种遍历⽅法主要的算法思想:1、先序遍历⼆叉树顺序:根节点 –> 左⼦树 –> 右⼦树,即先访问根节点,然后是左⼦树,最后是右⼦树。

上图中⼆叉树的前序遍历结果为:0 -> 1 -> 3 -> 4 -> 2 -> 5 -> 62、中序遍历⼆叉树顺序:左⼦树 –> 根节点 –> 右⼦树,即先访问左⼦树,然后是根节点,最后是右⼦树。

上图中⼆叉树的中序遍历结果为:3 -> 1 -> 4 -> 0 -> 5 -> 2 -> 63、后续遍历⼆叉树顺序:左⼦树 –> 右⼦树 –> 根节点,即先访问左⼦树,然后是右⼦树,最后是根节点。

上图中⼆叉树的后序遍历结果为:3 -> 4 -> 1 -> 5 -> 6 -> 2 -> 04、层序遍历⼆叉树顺序:从最顶层的节点开始,从左往右依次遍历,之后转到第⼆层,继续从左往右遍历,持续循环,直到所有节点都遍历完成上图中⼆叉树的层序遍历结果为:0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6下⾯是四种算法的伪代码:前序遍历:preOrderParse(int n) {if(tree[n] == NULL)return ; // 如果这个节点不存在,那么结束cout << tree[n].w ; // 输出当前节点内容preOrderParse(tree[n].leftChild); // 递归输出左⼦树preOrderParse(tree[n].rightChild); // 递归输出右⼦树}中序遍历inOrderParse(int n) {if(tree[n] == NULL)return ; // 如果这个节点不存在,那么结束inOrderParse(tree[n].leftChild); // 递归输出左⼦树cout << tree[n].w ; // 输出当前节点内容inOrderParse(tree[n].rightChild); // 递归输出右⼦树}pastOrderParse(int n) {if(tree[n] == NULL)return ; // 如果这个节点不存在,那么结束pastOrderParse(tree[n].leftChild); // 递归输出左⼦树pastOrderParse(tree[n].rightChild); // 递归输出右⼦树cout << tree[n].w ; // 输出当前节点内容}可以看到前三种遍历都是直接通过递归来完成,⽤递归遍历⼆叉树简答⽅便⽽且好理解,接下来层序遍历就需要动点脑筋了,我们如何将⼆叉树⼀层⼀层的遍历输出?其实在这⾥我们要借助⼀种数据结构来完成:队列。

二叉树遍历解题技巧

二叉树遍历解题技巧

二叉树遍历解题技巧
二叉树遍历是指按照一定规则,依次访问二叉树的所有节点的过程。

常见的二叉树遍历有前序遍历、中序遍历和后序遍历。

以下是一些二叉树遍历解题技巧:
1. 递归遍历:递归是最直观、最简单的遍历方法。

对于一个二叉树,可以递归地遍历其左子树和右子树。

在递归的过程中,可以对节点进行相应的处理。

例如,前序遍历可以先访问根节点,然后递归遍历左子树和右子树。

2. 迭代遍历:迭代遍历可以使用栈或队列来实现。

对于前序遍历,可以使用栈来记录遍历路径。

首先将根节点入栈,然后依次弹出栈顶节点,访问该节点,并将其右子节点和左子节点分别入栈。

中序遍历和后序遍历也可以使用类似的方法,只是访问节点的顺序会有所不同。

3. Morris遍历:Morris遍历是一种空间复杂度为O(1)的二叉树遍历方法。

它利用二叉树节点的空闲指针来存储遍历下一个节点的信息,从而避免使用额外的栈或队列。

具体步骤可以参考相关算法书籍或博客。

4. 层次遍历:层次遍历是一种逐层遍历二叉树的方法。

可以使用队列来实现。

首先将根节点入队,然后依次将队首节点出队并访问,同时将其左子节点和右子节点入队。

不断重复这个过程,直到队列为空。

层次遍历可以按照从上到下、从左到右的顺序访问二叉树的节点。

除了以上技巧,还可以根据具体问题的特点来选择合适的遍历方法。

在实际解题中,可以尝试不同的遍历方法并选择效率高、代码简洁的方法。

二叉树的先序遍历和中序遍历的非递归算法

二叉树的先序遍历和中序遍历的非递归算法
第 2 3卷
第 1期
电 脑 开 发 与 应 用
文 章编 号 :0 35 5 ( 00 9—0 30 1 0—8 0 2 1 ) 10 5 —3
二 叉树 的先 序 遍 历 和 中序 遍 历 的非 递 归 算 法
Di c s i n a s u s o nd Ana y i n— e u s v g r t m o e r r l s s of No r c r i e Al o ih f r Pr o de
t e S p e r rt a e s la t i r e’ r o de r v r a nd ob an non r c sv l ort o i ar r e’ e r ertav r a i t c A tls obt i ng non e ur i e a g ihm f r b n y t e Spr o d r e s lusng s a k. a t. ani
ta e s . r v r a1 The i p t c s an yssng oft e lf r bi r r e’ S pr or r tav r a d bi r r e’ S i r rt a er a . m oran e i al i i he r a o na y t e e de r e s lan na y t e no de r v s 1 K EYW O RDS bi r t e na y r e’ S pr or e t a r a , bi r t e e d r r ve s l na y r e’ a g ihm l ort
Pr o d ( 一 r hid); e r er bt> c l
从二 叉树 先 序遍 历非 递归 算法 实现 时 系统栈 的变 化情 况 , 我们 不难 看 出 , 二叉 树 先序遍 历 实 际上 是走 丫

数据结构中二叉树的生成及遍历非递归算法浅析

数据结构中二叉树的生成及遍历非递归算法浅析

及运算 都较为简练 , 因此 , 二叉树 在数据结构课 程 中显得 特别 c a dt; hr aa s ut to eci , hd t c bnd h dr i ; r l l cl 二叉树是 由结点的有 限集合构成 ,这个有限集合或者为空 }t e Br ; e 集 ,或者是 由一个根节点及两棵互不相交的分别称之为这个根 Bre [ as e t Q m xi ] e z;


引言
# c d “aoh il e m1 ・ nu ] ” c
t ee。 c b oe y d t t t d{ p n

二叉树是一种重要 的树形结构 , 其结构规整。许多实际问 # en U L0 df e L i N
题抽象 出来 的数据结构往往是二叉树 的形式 , 而且其存储结构 重要 , 这里 我们先 了解一下二叉树 。

立二 叉链表。 一般的二 对于 叉树, 必须添加一些 虚结点, 使其成 ‘ ~’ : 一 、
队列是一个指针类型 的数组 , 保存已输入 的结点 _… 、
… ~ … 一 ’

# e n x i 0 d f ema sz 1 0 i e 衔n l d sdoh” cu e“ t i.
s> 一
l= L ; d U L
r a+ ; e r +
Qra1s r ; e =

3 办公 自动化杂志 o。
i ra- 1T s f er= )= : ( =
es le
f=t kt ] T s c [p; a o
近 >i = p 卜 r =) 曲t
fr f“ " - dt ; pi (%c , > aa n T )
递归算法 , 故有关二叉树的试题通 常要求采用非递归算 法, 这就 Br , ; te e s 使得掌握二叉树的生成及遍历的非递归算法成为必要 。 tN I ; = uJ L
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据结构(双语)——项目文档报告用两种方式实现表达式自动计算专业:班级:指导教师:姓名:学号:目录一、设计思想 (01)二、算法流程图 (02)三、源代码 (04)四、运行结果 (11)五、遇到的问题及解决 (11)六、心得体会 (12)一、设计思想二叉树的遍历分为三种方式,分别是先序遍历,中序遍历和后序遍历。

先序遍历实现的顺序是:根左右,中序遍历实现的是:左根右,后续遍历实现的是:左右根。

根据不同的算法分,又分为递归遍历和非递归遍历。

递归算法:1.先序遍历:先序遍历就是首先判断根结点是否为空,为空则停止遍历,不为空则将左子作为新的根结点重新进行上述判断,左子遍历结束后,再将右子作为根结点判断,直至结束。

到达每一个结点时,打印该结点数据,即得先序遍历结果。

2.中序遍历:中序遍历是首先判断该结点是否为空,为空则结束,不为空则将左子作为根结点再进行判断,打印左子,然后打印二叉树的根结点,最后再将右子作为参数进行判断,打印右子,直至结束。

3.后续遍历:指针到达一个结点时,判断该结点是否为空,为空则停止遍历,不为空则将左子作为新的结点参数进行判断,打印左子。

左子判断完成后,将右子作为结点参数传入判断,打印右子。

左右子判断完成后打印根结点。

非递归算法:1.先序遍历:首先建立一个栈,当指针到达根结点时,打印根结点,判断根结点是否有左子和右子。

有左子和右子的话就打印左子同时将右子入栈,将左子作为新的根结点进行判断,方法同上。

若当前结点没有左子,则直接将右子打印,同时将右子作为新的根结点判断。

若当前结点没有右子,则打印左子,同时将左子作为新的根结点判断。

若当前结点既没有左子也没有右子,则当前结点为叶子结点,此时将从栈中出栈一个元素,作为当前的根结点,打印结点元素,同时将当前结点同样按上述方法判断,依次进行。

直至当前结点的左右子都为空,且栈为空时,遍历结束。

2.中序遍历:首先建立一个栈,定义一个常量flag(flag为0或者1),用flag记录结点的左子是否去过,没有去过为0,去过为1,默认为0.首先将指针指向根结点,将根结点入栈,然后将指针指向左子,左子作为新的结点,将新结点入栈,然后再将指针指向当前结点的左子,直至左子为空,则指针返回,flag置1,出栈一个元素,作为当前结点,打印该结点,然后判断flag,flag为1则将指针指向当前结点右子,将右子作为新的结点,结点入栈,再次进行上面的判断,直至当前结点右子也为空,则再出栈一个元素作为当前结点,一直到结束,使得当前结点右子为空,且栈空,遍历结束。

3.后续遍历:首先建立两个栈,然后定义两个常量。

第一个为status,取值为0,1,2.0代表左右子都没有去过,1代表去过左子,2,代表左右子都去过,默认为0。

第二个常量为flag,取值为0或者1,0代表进左栈,1代表进右栈。

初始时指针指向根结点,判断根结点是否有左子,有左子则,将根结点入左栈,status置0,flag置0,若没有左子则判断结点有没有右子,有右子就把结点入右栈,status置0,flag置1,若左右子都没有,则打印该结点,并将指针指向空,此时判断flag,若flag为0,则从左栈出栈一个元素作为当前结点,重新判断;若flag为1则从右栈出栈一个元素作为当前结点,重新判断左右子是否去过,若status 为1,则判断该结点有没有右子,若有右子,则将该结点入右栈,status置1,flag置1,若没有右子,则打印当前结点,并将指针置空,然后再次判断flag。

若当前结点status为2,且栈为空,则遍历结束。

若指针指向了左子,则将左子作为当前结点,判断其左右子情况,按上述方法处理,直至遍历结束。

二、算法流程图图1 二叉树的建立用先序方法建立二叉树,为每个结点定义左右子,用0代表空,得到上述二叉树图2 非递归二叉树遍历先序首先建立一个栈,当指针到达根结点时,打印根结点,判断根结点是否有左子和右子。

有左子和右子的话就打印左子同时将右子入栈,将左子作为新的根结点进行判断,方法同上。

若当前结点没有左子,则直接将右子打印,同时将右子作为新的根结点判断。

若当前结点没有右子,则打印左子,同时将左子作为新的根结点判断。

若当前结点既没有左子也没有右子,则当前结点为叶子结点,此时将从栈中出栈一个元素,作为当前的根结点,打印结点元素,同时将当前结点同样按上述方法判断,依次进行。

直至当前结点的左右子都为空,且栈为空时,遍历结束。

图3 非递归二叉树遍历中序中序遍历:首先建立一个栈,定义一个常量flag(flag为0或者1),用flag记录结点的左子是否去过,没有去过为0,去过为1,默认为0.首先将指针指向根结点,将根结点入栈,然后将指针指向左子,左子作为新的结点,将新结点入栈,然后再将指针指向当前结点的左子,直至左子为空,则指针返回,flag置1,出栈一个元素,作为当前结点,打印该结点,然后判断flag,flag为1则将指针指向当前结点右子,将右子作为新的结点,结点入栈,再次进行上面的判断,直至当前结点右子也为空,则再出栈一个元素作为当前结点,一直到结束,使得当前结点右子为空,且栈空,遍历结束。

图4 非递归二叉树遍历后序首先建立两个栈,然后定义两个常量。

第一个为status,取值为0,1,2.0代表左右子都没有去过,1代表去过左子,2,代表左右子都去过,默认为0。

第二个常量为flag,取值为0或者1,0代表进左栈,1代表进右栈。

初始时指针指向根结点,判断根结点是否有左子,有左子则,将根结点入左栈,status置0,flag置0,若没有左子则判断结点有没有右子,有右子就把结点入右栈,status置0,flag置1,若左右子都没有,则打印该结点,并将指针指向空,此时判断flag,若flag为0,则从左栈出栈一个元素作为当前结点,重新判断;若flag 为1则从右栈出栈一个元素作为当前结点,重新判断左右子是否去过,若status为1,则判断该结点有没有右子,若有右子,则将该结点入右栈,status置1,flag置1,若没有右子,则打印当前结点,并将指针置空,然后再次判断flag。

若当前结点status为2,且栈为空,则遍历结束。

若指针指向了左子,则将左子作为当前结点,判断其左右子情况,按上述方法处理,直至遍历结束。

三、源代码下面给出的是用递归算法实现的程序的源代码:#include<stdio.h>#include<stdlib.h>//用递归的方式遍历二叉树typedef struct node //定义二叉树的结点{ int data; //结点的数据struct node*lChild,*rChild; //结点左右子}Node;int i=-1; //控制下面函数中循环的Node *buildTree(int *b) //产生二叉树(利用先序递归产生){Node *p; //创建一个根结点指针if(b[++i]==0)p=NULL; //如果传入的当前值为0 则设其为空结点else{ p=(Node*)malloc(sizeof(Node)); //开辟内存p->data=b[i]; //设置当前结点的数据p->lChild=buildTree(b); //左子结点p->rChild=buildTree(b); //右子}return p; //把创建的树的根节点返回}void preOrder(Node *root) //前序遍历{if(root!=0) //如果根节点不为0{printf("%d ",root->data); //打印当前结点preOrder(root->lChild); //指向左子preOrder(root->rChild); //指向右子}}void inOrder(Node *root) //中序遍历{if(root!=0) //如果根节点不为0{inOrder(root->lChild); //指向左子printf("%d ",root->data); //打印当前结点inOrder(root->rChild); //指向右子}}void postOrder(Node *root){if(root!=0){postOrder(root->lChild); //指向左子postOrder(root->rChild); //指向右子printf("%d ",root->data); //打印当前结点}}void main(){//按先序次序输入树的结点(非0整数)来创建一个树空结点用0表示int a[] = {1,2,4,0,7,0,0,0,3,5,0,0,6,8,0,0,9,0,0};int *b = a;//将指向数组首地址的指针传给bulidTree 函数来创建树Node *root = buildTree(b);printf("用递归方法\n\n前序遍历: "); //打印提示内容preOrder(root); //调用前序遍历函数printf("\n中序遍历: "); //打印提示内容inOrder(root); //调用中序遍历函数printf("\n后序遍历: "); //打印提示内容postOrder(root); //调用后序遍历函数getch();}下面给出的是用非递归算法实现的程序的源代码:#include<stdio.h>#include<stdlib.h>//用非递归的方式遍历二叉树typedef struct node //定义二叉树的结点{ int data; //结点的数据struct node *lChild,*rChild; //结点左右子}Node;typedef struct //创建栈{Node *bottom; //栈底指针Node *top; //栈顶指针}Stack;void init(Stack *s) //初始化栈{s->bottom=(Node *)malloc(100*sizeof(Node)); //为指针开辟内存s->top=s->bottom; //栈顶指针指向栈底指针}int isEmpty(Stack s) //判断栈是否为空的函数{if(s.top==s.bottom)return 1; //栈空返回1elsereturn 0; //不为空返回0}void push(Stack *s,Node node) //栈的push方法{*(s->top++)=node; //给栈顶赋值然后top+1}Node pop(Stack *s) //出栈函数{Node node; //声明一Node类型遍量node=*(--(s->top)); //node 为栈顶元素然后top-1 return node; //返回pop出的结点}Node peek(Stack *s) //看栈顶元素{return *(s->top-1); //返回栈顶元素}typedef struct //创建栈(MyStack)结构体{Node *bottom; //栈底指针Node *top; //栈顶指针}MyStack;void init1(MyStack *s) //初始化栈{s->bottom=(Node *)malloc(100*sizeof(Node)); //开辟内存s->top=s->bottom; //栈顶指针指向栈底指针}void push1(MyStack *s,Node node) //进栈方法{*(s->top++)=node; //给栈顶赋值然后top+1}Node pop1(MyStack *s) //出栈函数{Node node; //声明一Node类型遍量node=*(--(s->top)); //node 为栈顶元素然后top-1 return node; //返回pop出的结点}Node peek1(MyStack *s) //查栈顶元素{return *(s->top-1); //返回栈顶元素}int isEmpty1(MyStack s) //判断栈是否为空{if(s.top==s.bottom)return 1; //栈空了返回1 elsereturn 0; //不为空返回0}int temp=-1;Node *buildTree(int *b) //产生二叉树{Node *p; //创建一个根结点指针if(b[++temp]==0)p=NULL; //如果传入的当前值为0 则设其为空结点else{ p=(Node*)malloc(sizeof(Node)); //开辟内存p->data=b[temp]; //设置当前结点的数据p->lChild=buildTree(b); //左子结点p->rChild=buildTree(b); //右子};return p; //把创建的树的根结点返回}void preOrder(Node *root) //前序遍历{Stack po; //声明一个栈Node curr = *root; //当前结点为根结点init(&po); //初始化找while(curr.data!=0||!isEmpty(po)) //当前结点不为空且栈不为空{if(curr.data==0) //如果当前结点为空{curr=pop(&po); //当前结点指向pop出栈的结点}if(curr.rChild!=NULL) //如果右子为空{push(&po,*curr.rChild); //将右子进栈}printf("%d ",curr.data); //打印当前结点的内容if(curr.lChild!=NULL) //如果左子不为空{curr=*curr.lChild; //当前子指向左子}else{curr=pop(&po); //当前子指向pop出栈结点}if((curr.lChild==NULL)&&(curr.rChild==NULL)) //如果左子右子都为空{printf("%d ",curr.data); //打印当前结点的内容curr.data=0; //当前结点置空}}}void inOrder(Node *root) //中序遍历{Stack ms; //声明一个栈Node curr = *root; //当前结点指向根结点int flag = 0;//设置一个标志0:当前结点指向了右结点1:当前结点指向了左结点init(&ms); //初始化栈while(curr.data!=0||isEmpty(ms)) //当前结点不为空且栈不为空{if(curr.lChild!=NULL&&flag==0) //左子不为空且没去过左子{push(&ms,curr); //当前子进栈curr=*curr.lChild; //当前结点指向左子}else{printf("%d ",curr.data); //打印当前结点的内容if(curr.rChild!=NULL) //左子为空{curr=*curr.rChild; //指向左子}flag=0; //flag 置0}if(curr.rChild==NULL&&curr.lChild==NULL) //如果左右子都为空{printf("%d ",curr.data); //打印当前结点的内容if(isEmpty(ms)==1) break; //栈空则结束循环curr = pop(&ms); //当前子指向pop出栈的结点flag=1; //flag 置1}}}void postOrder(Node *root) //后序遍历{//声明左右栈如果当前结点有左子则进左栈若没左子但是有右子则进右栈Stack msl; //声明左栈MyStack msr; //声明右栈Node curr = *root; //结点指向树的根结点int flag=0;//设置一个标志0:进左栈1:进右栈//设置一个标志0:没去过左右子树1:去过左子树2:去过右子树(两子树都去过) int status=0;init(&msl); //初始化左栈init(&msr); //初始化右栈while(curr.data!=0||isEmpty(msl)!=0||isEmpty1(msr)!=0)//当前结点不为空且左右栈都不为空{if(status==0&&curr.lChild!=NULL) //没去过左右子树且右子不为空{push(&msl,curr); //当前子进左栈curr = *curr.lChild; //当前子指向左子flag=0; //flag置0}else if(status!=2&&curr.rChild!=NULL) //没去过右子树且右子不为空{push1(&msr,curr); //当前子进右栈curr=*curr.rChild; //当前子指向右子flag=1; //flag置1status=0; //status 置0}else{printf("%d ",curr.data); //打印当前结点内容curr.data=0; //当前结点置空}if(curr.data==0) //如果当前子为空{ if(flag==0) //如果flag标志为0{if(isEmpty(msl)==0) //如果左栈不为空{curr = pop(&msl); //指向左栈弹出的元素status=1; //status标志置为1}else if(isEmpty1(msr)==0){curr = pop1(&msr); //指向右栈弹出的元素status=2; //status标志置为2}}else{if(isEmpty1(msr)==0) //如果右栈为空{curr=pop1(&msr); //指向右栈弹出的元素status=2;}else if(isEmpty(msl)==0){curr=pop(&msl); //指向左栈弹出的元素status=1; //status标志置为1}}if(curr.data==0) break; //若当前结点为空,结束循环}}}void main(){int Tree[] = {1,2,4,0,7,0,0,0,3,5,0,0,6,8,0,0,9,0,0};int *tree = Tree;Node *root = buildTree(tree); //创建一个结点指向创建的树的根结点printf("用非递归方法\n前序遍历: "); //打印提示内容preOrder(root); //调用前序遍历函数printf("\n中序遍历: "); //打印提示内容inOrder(root); //调用中序遍历函数printf("\n后序遍历: "); //打印提示内容postOrder(root); //调用后序遍历函数getch();}四、运行结果图5 递归算法运行结果图图6 非递归算法运行结果图五、遇到的问题及解决这部分我主要遇到了如下两个问题,其内容与解决方法如下所列:●二叉树的建立在刚开始进行时,如何建立二叉树难住了我,上课时听老师讲的是java的,在建立二叉树时和C语言不一样,在网上查阅了一部分资料后,找到了合适的办法,就是用结构体来储存二叉树,结构体内定义了二叉树的值和左右子,用0代表null,这样就可以用先序算法遍历输入了●在进行非递归遍历时,编译不报错,但运行出现下面的错误到网上查询后,得知出现“Access Violation”错误,有两种可能,第一种:出现这个错误是因为试图访问一块已经被释放的内存,第二种是想使用一个还未创建对象的指针。

相关文档
最新文档