用递归非递归两种方法遍历二叉树
二叉树后序遍历的递归和非递归算法

安 徽电气工 程 职 业 技术学 院学报
:薹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 访问根结点 ( ) 若存在根结点)
二叉树数据结构如下 :
二叉树是数据结构 中最常见 的存储形式 , 在算法与数据结构中经常使用。树与森林都可以转换为 二叉树 , 而遍历算法则是二叉树最重要的操作 。所谓遍历二叉树 , 就是遵从某种次序 , 遍访二叉树 中的
所有结点, 使得每个结点被访问一次 , 而且仅一次。在遍历算法中, 递归算法是最普遍 的, 弄清 了递归算
二叉树的遍历及常用算法

⼆叉树的遍历及常⽤算法⼆叉树的遍历及常⽤算法遍历的定义:按照某种次序访问⼆叉树上的所有结点,且每个节点仅被访问⼀次;遍历的重要性:当我们需要对⼀颗⼆叉树进⾏,插⼊,删除,查找等操作时,通常都需要先遍历⼆叉树,所有说:遍历是⼆叉树的基本操作;遍历思路:⼆叉树的数据结构是递归定义(每个节点都可能包含相同结构的⼦节点),所以遍历也可以使⽤递归,即结点不为空则继续递归调⽤每个节点都有三个域,数据与,左孩⼦指针和右孩⼦之指针,每次遍历只需要读取数据,递归左⼦树,递归右⼦树,这三个操作三种遍历次序:根据访问三个域的不同顺序,可以有多种不同的遍历次序,⽽通常对于⼦树的访问都按照从左往右的顺序;设: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 个结点。为了便于理解遍历思想 , 暂时为每个没有 予树 的结点都
f
e ely e b s 1 Ⅱ p 赤 a e: t e e t p *t p' lmye o ,
树和二叉树的知识点总结

树和二叉树的知识点总结一、树的基本概念1. 树的定义:树是一种非线性数据结构,由 n(n>=1)个结点组成的有限集合。
对于每个非终端节点,都有一个被称为根的结点,且除根节点外,其他结点可以分为 m(m>=0)个互不相交的子集合,而每个子集合本身又是一个树。
2. 树的基本特点:树是一种分层数据的抽象模型,具有层级关系的数据结构。
树的结点包括根结点、子节点、叶子结点、父节点等。
3. 树的术语解释:树的根节点是树的顶端结点,没有父节点;子节点是一个结点向下连接的结点;叶子结点是没有子节点的结点;父节点是有一个或多个子节点的结点。
二、树的分类1. 二叉树:一种特殊的树,每个结点最多有两个子结点,分别为左子结点和右子结点。
二叉树的子树有左子树和右子树,必须遵循左子树 < 根节点 < 右子树的顺序。
2. 平衡树:每个结点的左子树和右子树的高度之差不能超过1的二叉树。
3. 满二叉树:每个结点要么没有子节点,要么有两个子节点的二叉树。
4. 完全二叉树:除了最底层,所有层的结点数都达到最大,并且最底层的结点都依次从左到右排列。
三、二叉树的基本概念1. 二叉树的特点:每个结点最多有两个子结点,分别为左子结点和右子结点。
二叉树的子树都遵循左子树 < 根节点 < 右子树的顺序。
2. 二叉树的遍历:分为前序遍历、中序遍历和后序遍历。
前序遍历先访问根节点,再递归左右子树;中序遍历先递归左子树,再访问根节点,最后递归右子树;后序遍历先递归左右子树,最后访问根节点。
3. 二叉树的存储:二叉树的存储方式可以采用链式存储和顺序存储。
链式存储是通过结点间的指针链接,顺序存储是通过数组或列表进行存储。
四、二叉树的应用1. 二叉搜索树:是一种特殊的二叉树结构,对于任意节点,其左子树上的结点值都小于该节点的值,右子树上的结点值都大于该节点的值。
2. 堆:是一种特殊的完全二叉树,分为最大堆和最小堆。
最大堆的每个结点的值都大于或等于其子节点的值,最小堆的每个结点的值都小于或等于其子节点的值。
二叉树结点计算方法

二叉树结点计算方法二叉树是一种常见的数据结构,它由结点和连接结点的边组成。
每个结点最多有两个子结点,称为左子结点和右子结点。
在二叉树中,每个结点都有一个值,可以用来存储任何类型的数据。
计算二叉树结点的方法主要有以下几种:1.求二叉树的结点个数:-递归法:计算二叉树的结点个数可以使用递归的方式,首先判断根结点是否为空,如果为空,则返回0;否则,返回左子树的结点个数加上右子树的结点个数再加1,即根结点自身的个数。
递归地计算左右子树的结点个数,直到叶子结点为空,递归结束。
2.求二叉树的叶子结点个数:-递归法:计算二叉树的叶子结点个数也可以使用递归的方式,首先判断根结点是否为空,如果为空,则返回0;否则,如果根结点的左右子树都为空,则返回1,表示根结点为叶子结点。
递归地计算左右子树的叶子结点个数,通过累计求和的方式得到最终的结果。
3.求二叉树的深度:-递归法:计算二叉树的深度可以使用递归的方式,首先判断根结点是否为空,如果为空,则返回0;否则,分别计算左子树和右子树的深度,然后取两者中的较大值,再加上根结点自身的深度,即可得到二叉树的深度。
递归地计算左右子树的深度,直到叶子结点为空,递归结束。
4.求二叉树的最小深度:-递归法:计算二叉树的最小深度可以使用递归的方式,首先判断根结点是否为空,如果为空,则返回0;否则,如果根结点的左右子树都为空,则返回1,表示根结点为叶子结点。
如果根结点的左子树为空,则取右子树的最小深度;如果根结点的右子树为空,则取左子树的最小深度;否则,取左右子树中的较小深度。
递归地计算左右子树的最小深度,通过取较小值的方式得到最终的结果。
以上是常见的计算二叉树结点的方法,它们都可以通过递归的方式实现。
在实际应用中,可以根据具体的需求选择适当的方法来计算二叉树的结点。
后序遍历序列算法实现

后序遍历序列算法实现后序遍历是二叉树遍历的一种方式,它的遍历顺序是先访问左子树,再访问右子树,最后访问根节点。
因此,后序遍历的顺序是左子树、右子树、根节点。
算法实现:后序遍历可以用递归和非递归两种方式来实现。
1. 递归实现递归实现后序遍历的思路比较简单,只需要按照左子树、右子树、根节点的顺序递归遍历即可。
具体实现如下:```void postOrderTraversal(TreeNode* root) {if (root != NULL) {postOrderTraversal(root->left); // 遍历左子树postOrderTraversal(root->right); // 遍历右子树cout << root->val << " "; // 访问当前节点}```2. 非递归实现非递归实现后序遍历需要借助一个辅助数据结构——栈。
具体思路是先将整个二叉树从上到下、从左到右依次入栈,然后每次取出一个节点时判断其是否为叶子节点或者其左右子节点是否已经被访问过了。
如果是叶子节点或者其左右子节点已经被访问过了,就可以直接访问该节点;否则,需要将其右子节点、左子节点分别入栈,保证后续遍历的顺序。
具体实现如下:```void postOrderTraversal(TreeNode* root) {if (root == NULL) {return;}stack<TreeNode*> s;s.push(root);TreeNode* pre = NULL;while (!s.empty()) {TreeNode* cur = s.top();if ((cur->left == NULL && cur->right == NULL) || (pre != NULL && (pre == cur->left || pre == cur->right))) {cout << cur->val << " ";s.pop();pre = cur;}else {if (cur->right != NULL) {s.push(cur->right);}if (cur->left != NULL) {s.push(cur->left);}}}}```总之,无论是递归还是非递归实现后序遍历,都需要按照左子树、右子树、根节点的顺序进行遍历。
非递归中序遍历二叉树课件

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

二叉树的遍历学习心得 (3)
二叉树是一种经常出现在程序设计中的数据结构。
通过对二叉树的遍历,能够完成许多复杂的任务。
因此,学习二叉树的遍历方式非常重要。
首先,二叉树的遍历包括前序遍历、中序遍历和后序遍历。
其中,前序遍历就是先遍历根节点,再遍历左节点和右节点。
中序遍历是先遍历左节点,再遍历根节点和右节点。
后序遍历是先遍历左节点,再遍历右节点和根节点。
我们需要掌握这三种遍历方式,了解其原理和具体实现方法。
其次,在实现遍历方式时,我们需要使用递归或非递归的方式。
递归方式简单易懂,但是当节点较多时,会占用大量的栈空间,会导致栈溢出。
而非递归方式需要使用辅助数据结构,比如栈或队列,来实现遍历。
虽然它的代码相对复杂,但却具有灵活性和高效性。
我们要根据具体情况,选择适合的遍历方式。
最后,我们还需要注意二叉树遍历的应用。
比如,前序遍历可以用于复制树或表达式树,中序遍历可以用于对树进行排序,后序遍历可以用于计算表达式树的值等。
因此,在学习二叉树遍历的同时,还要了解它们的常见应用场景,以便更好地进行算法设计和应用实践。
总之,掌握二叉树的遍历方式和应用,对于数据结构和算法的学习非常重要。
我们要理解其原理和代码实现,并且要多加练习,深入掌握遍历的思想和技巧。
只有这样,我们才能在实际工作中灵活运用二叉树遍历,提高代码质量和效率。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构(双语)——项目文档报告用递归、非递归两种方法遍历二叉树专业:计算机科学与技术班级:指导教师:姓名:学号:目录一、设计思想 (03)二、算法流程图 (04)三、源代码 (06)四、运行结果 (12)五、遇到的问题及解决 (14)六、心得体会 (15)一、设计思想1.递归:(1)主函数main()主程序要包括:定义的二叉树T、建树函数、先序遍历函数、中序遍历函数、后序遍历函数。
(2)建树函数定义一个输入的数是字符型的,当ch为空时,T就为空值,否则的话就分配空间给T,T就指向它的结点,然后左指针域指向左孩子,右指针指向右孩子,若还有,继续调用,依次循环下去,直到ch遇到空时,结束。
最后要返回建立的二叉树T。
(3)先序遍历函数根据先序遍历规则,当T为非空时,先输出结点处的数据,指针指向左、右孩子,依次进行下去。
(4) 中序遍历函数根据中序遍历规则,当T为非空时,先左指针指向左孩子数据,然后输出结点处的数据,再右指针指向右孩子,依次进行下去。
(5)后序遍历函数根据后序遍历规则,当T为非空时,先右指针指向右孩子,然后左指针指向左孩子,最后输出结点处的数据,依次进行下去。
2.非递归:(1)跟递归遍历二叉树的前提一样,首先应该创建一个二叉树,同样使用先序递归的方式创建二叉树。
(2)然后是中序,先序,后序非递归遍历二叉树。
(3)中序非递归遍历二叉树的思想是:首先是根节点压栈,当根节点的左子树不是空的时候,左子树压栈。
直到左子树为空的时候,不再压栈。
将栈顶元素出栈,访问栈顶元素,并将栈顶的右子树进栈。
当右子树的左子树不是空的时候,左子树一直进栈,直到左子树为空,则不再进栈。
重复上面的操作,直到栈空的时候。
(4)先序非递归遍历二叉树的思想是:首先是根节点进栈,然后当栈不为空的时候,将栈顶元素出栈,然后访问。
同时将出栈元素的右子树进栈,左子树进栈。
重复上面的操作,直到栈为空。
(5)后序非递归遍历二叉树的思想:首先是根节点进栈,当根节点的左子树不为空的时候,左子树进栈,直到左为空的时候,左子树不再进栈。
指针指向的是右子树,当右子树为空的时候,直接访问根节点。
当右子树不为空的时候,则右子树的指针进栈,当右子树的左子树不为空的时候,则左也进栈,直到左为空。
重复上面的操作,直到栈为空的时候,则遍历树完成。
二、算法流程图1.递归2.非递归三、源代码1.递归#include "stdio.h"#include "conio.h"#include <stdlib.h>#include<stack>typedef struct node{char data;struct node *lchild, *rchild;}BinTnode;typedef BinTnode * BinTree; //定义二叉树类型的指针/*先序创建二叉树*/int CreateBinTree(BinTree *T){char ch;*T=(BinTree)malloc(sizeof(BinTnode));if(!*T) printf("overflow");do{ch=getchar();if(ch==' '){ *T=NULL;return 0;}else{(*T)->data=ch;CreateBinTree(&((*T)->lchild));CreateBinTree(&((*T)->rchild));return 1;}}while(ch!='\0');}/*中序递归遍历*/void InorderTransverse(BinTree s){if (s){InorderTransverse(s->lchild);printf("%c", s->data);InorderTransverse(s->rchild);}}//先序递归遍历二叉树void PreOrderTranverseTree(BinTree s){if (s){printf("%c", s->data);PreOrderTranverseTree(s->lchild);PreOrderTranverseTree(s->rchild);}}//后序递归遍历二叉树void PostOrderTranverseTree(BinTree s){if (s){PreOrderTranverseTree(s->lchild);PreOrderTranverseTree(s->rchild);printf("%c", s->data);}}/*主方法*/void main(){BinTree T;printf("请按照先序的顺序输入要创建的树:\n");CreateBinTree(&T); /*中序序列创建二叉树*/printf("中序递归遍历的序列是:");InorderTransverse(T);printf("\n");//先序递归遍历printf("先序递归遍历的序列是:");PreOrderTranverseTree(T);printf("\n");//后序递归遍历printf("后序递归遍历的序列是:");PostOrderTranverseTree(T);printf("\n");}2.非递归#include "stdio.h"#include "conio.h"#include <stack>#include <stdlib.h>typedef struct node{char data;struct node *lchild, *rchild;}BinTnode;typedef BinTnode * BinTree; //定义二叉树类型的指针typedef struct{BinTree data[100];int top;}SeqStack;void initStack(SeqStack *S){S->top =-1;}void Push(SeqStack *S,BinTree x){if(S->top==100-1){printf("the stack is overflow");}else {S->top=S->top+1;S->data[S->top]=x;}}int Pop(SeqStack *S,BinTree *p){if(S->top==-1){printf("the stack is underflow");return 0;}else {*p=S->data[S->top];--S->top;return 1;}}int EmptyStack(SeqStack S){if(S.top==-1) return 1;else return 0; /* 栈不空的情况*/}int GetTop(SeqStack S,BinTree *p){if(S.top==-1){printf("the stack is empty");return 0;}else {*p=S.data[S.top];return 1;}}char visit(BinTree p){return (*p).data;}/*创建二叉树*/int CreateBinTree(BinTree *T){char ch;*T=(BinTree)malloc(sizeof(BinTnode));if(!*T) printf("overflow");else{do{ch=getchar();if(ch!=' '){*T=NULL;return 0;}else{(*T)->data=ch;CreateBinTree(&((*T)->lchild));CreateBinTree(&((*T)->rchild));return 1;}}while(ch!='\0');}}/*中序非递归遍历*/void InorderTransverse(BinTree T){SeqStack S;BinTree p;initStack(&S);//初始化栈printf("中序非递归序列是:");Push(&S,T); //根指针进栈 T为指向二叉树的指针while(!EmptyStack(S)){ //栈不是空的情况while(GetTop(S,&p) && p)Push(&S,p->lchild); //gettop得到的结果也必须是一棵子树才行 ,进栈应该进的是树根的指针Pop(&S,&p);if(!EmptyStack(S)){//printf("%c",visit(p));Pop(&S,&p);printf("%c",visit(p));Push(&S,p->rchild);}}}/*先序非递归遍历*/void PreorderTransverse(BinTree T){SeqStack S;BinTree p;initStack(&S);//初始化栈Push(&S,T); //根指针进栈 T为指向二叉树的指针printf("先序非递归序列是:");while(!EmptyStack(S)){Pop(&S,&p); //根节点出栈if(p!=NULL){printf("%c",visit(p));Push(&S,p->rchild);Push(&S,p->lchild);}}}/*后序非递归遍历*/void PostorderTransverse(BinTree T){SeqStack S;BinTree p,q;initStack(&S);//初始化栈p=T;printf("后序非递归序列是:");while(p ||!EmptyStack(S)){ //跳出while循环的原因是因为左子树或者右子树为空了if(p!=q){while(p!=NULL){Push(&S,p);if(p->lchild!=NULL)p=p->lchild;elsep=p->rchild;}}if(EmptyStack(S)) break;GetTop(S,&q);if(q->rchild==p){ //进栈的是右子树Pop(&S,&p);printf("%c",visit(p));p=q;}else{p=q->rchild;}}}/*主方法*/void main(){BinTree T;printf("请按照先序的顺序输入创建的树:\n");/*创建树*/CreateBinTree(&T);//中序非递归遍历InorderTransverse(T);printf("\n");//先序非递归遍历PreorderTransverse(T);printf("\n");//后序非递归遍历PostorderTransverse(T);}四、运行结果1.递归输入结果2.非递归输入结果五、遇到的问题及解决经过一个星期的写代码,我遇到了很多问题,并一一解决了,比如:1.创建二叉树时:void createBiTree(BiTNode *T)和void createBiTree(BiTNode *&T)没分清楚区别。