非递归后序遍历二叉树共41页文档
- 格式:ppt
- 大小:3.60 MB
- 文档页数:41
后序遍历的非递归算法(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。
⼆叉树遍历(前序、中序、后序、层次、⼴度优先、深度优先遍历)⽬录转载:⼆叉树概念⼆叉树是⼀种⾮常重要的数据结构,⾮常多其他数据结构都是基于⼆叉树的基础演变⽽来的。
对于⼆叉树,有深度遍历和⼴度遍历,深度遍历有前序、中序以及后序三种遍历⽅法,⼴度遍历即我们寻常所说的层次遍历。
由于树的定义本⾝就是递归定义,因此採⽤递归的⽅法去实现树的三种遍历不仅easy理解并且代码⾮常简洁,⽽对于⼴度遍历来说,须要其他数据结构的⽀撑。
⽐⽅堆了。
所以。
对于⼀段代码来说,可读性有时候要⽐代码本⾝的效率要重要的多。
四种基本的遍历思想前序遍历:根结点 ---> 左⼦树 ---> 右⼦树中序遍历:左⼦树---> 根结点 ---> 右⼦树后序遍历:左⼦树 ---> 右⼦树 ---> 根结点层次遍历:仅仅需按层次遍历就可以⽐如。
求以下⼆叉树的各种遍历前序遍历:1 2 4 5 7 8 3 6中序遍历:4 2 7 5 8 1 3 6后序遍历:4 7 8 5 2 6 3 1层次遍历:1 2 3 4 5 6 7 8⼀、前序遍历1)依据上⽂提到的遍历思路:根结点 ---> 左⼦树 ---> 右⼦树,⾮常easy写出递归版本号:public void preOrderTraverse1(TreeNode root) {if (root != null) {System.out.print(root.val+" ");preOrderTraverse1(root.left);preOrderTraverse1(root.right);}}2)如今讨论⾮递归的版本号:依据前序遍历的顺序,优先訪问根结点。
然后在訪问左⼦树和右⼦树。
所以。
对于随意结点node。
第⼀部分即直接訪问之,之后在推断左⼦树是否为空,不为空时即反复上⾯的步骤,直到其为空。
若为空。
则须要訪问右⼦树。
注意。
在訪问过左孩⼦之后。
⼆叉树遍历(前中后序遍历,三种⽅式)⽬录刷题中碰到⼆叉树的遍历,就查找了⼆叉树遍历的⼏种思路,在此做个总结。
对应的LeetCode题⽬如下:,,,接下来以前序遍历来说明三种解法的思想,后⾯中序和后续直接给出代码。
⾸先定义⼆叉树的数据结构如下://Definition for a binary tree node.struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(NULL), right(NULL) {}};前序遍历,顺序是“根-左-右”。
使⽤递归实现:递归的思想很简单就是我们每次访问根节点后就递归访问其左节点,左节点访问结束后再递归的访问右节点。
代码如下:class Solution {public:vector<int> preorderTraversal(TreeNode* root) {if(root == NULL) return {};vector<int> res;helper(root,res);return res;}void helper(TreeNode *root, vector<int> &res){res.push_back(root->val);if(root->left) helper(root->left, res);if(root->right) helper(root->right, res);}};使⽤辅助栈迭代实现:算法为:先把根节点push到辅助栈中,然后循环检测栈是否为空,若不空,则取出栈顶元素,保存值到vector中,之后由于需要想访问左⼦节点,所以我们在将根节点的⼦节点⼊栈时要先经右节点⼊栈,再将左节点⼊栈,这样出栈时就会先判断左⼦节点。
代码如下:class Solution {public:vector<int> preorderTraversal(TreeNode* root) {if(root == NULL) return {};vector<int> res;stack<TreeNode*> st;st.push(root);while(!st.empty()){//将根节点出栈放⼊结果集中TreeNode *t = st.top();st.pop();res.push_back(t->val);//先⼊栈右节点,后左节点if(t->right) st.push(t->right);if(t->left) st.push(t->left);}return res;}};Morris Traversal⽅法具体的详细解释可以参考如下链接:这种解法可以实现O(N)的时间复杂度和O(1)的空间复杂度。
二叉树后序遍历的非递归算法
二叉树后序遍历是指按照左子树、右子树、根节点的顺序遍历二叉树的过程。
与前序遍历和中序遍历不同,后序遍历需要考虑根节点的位置,因此需要使用栈来存储节点信息。
非递归算法一般使用栈来实现,因为后序遍历的过程中需要先遍历左子树和右子树,最后才遍历根节点,所以存储节点信息的栈需要进行一些特殊处理。
下面是二叉树后序遍历的非递归算法:
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. 新建一个栈,并将根节点入栈2. 定义两个节点变量pre和cur,初始化pre为null3. 当栈不为空时,循环执行以下操作:- 将栈顶元素cur赋值为栈顶元素,但不弹出该元素- 如果当前节点没有左右子节点,或者左右子节点已经被访问过了,那么弹出当前节点,并将其值打印输出,并将pre赋值为当前节点- 否则,若当前节点有右子节点,就将其右子节点入栈。
若当前节点有左子节点,则将其左子节点入栈4. 循环结束可以看到,后序遍历的算法和前序遍历、中序遍历都有所区别。
与前序遍历的主要区别在于,在访问节点前,需要判断该节点的左右子节点是否已经被访问过。
而与中序遍历的主要区别在于,在访问节点后,需要将该节点的值打印输出。
此外,后序遍历还需要维护一个pre节点变量,用于记录上一个被访问过的节点。
那么,后序遍历的非递归算法有什么优点呢?相比递归算法,它的空间复杂度更低,因为递归算法需要维护函数调用栈。
而非递归算法中使用的栈只需要在遍历过程中存储节点,不需要再维护函数调用栈。
此外,非递归算法在一些嵌入式系统、服务器等资源受限的环境下表现更优秀。
总体而言,后序遍历非递归算法是一种非常实用的二叉树遍历算法,它可以帮助我们更加高效地对二叉树进行遍历,尤其是在空间限制较大的情况下。
需要注意的是,该算法的具体实现过程可能会因为树结构的复杂性而略有差异,建议大家在编写代码时用心梳理整个算法过程。
⼆叉树的四种遍历算法⼆叉树作为⼀种重要的数据结构,它的很多算法的思想在很多地⽅都⽤到了,⽐如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 ; // 输出当前节点内容}可以看到前三种遍历都是直接通过递归来完成,⽤递归遍历⼆叉树简答⽅便⽽且好理解,接下来层序遍历就需要动点脑筋了,我们如何将⼆叉树⼀层⼀层的遍历输出?其实在这⾥我们要借助⼀种数据结构来完成:队列。
浅析一种二叉树非递归遍历算法的C语言实现论文一种二叉树非递归遍历算法的C语言实现论文导读:本论文是一篇关于一种二叉树非递归遍历算法的C语言实现的优秀论文范文,对正在写有关于递归论文的写有一定的参考和指导作用,摘要:针对二叉树的链式存储结构,分析了二叉树的各种遍历算法,探讨了递归算法的递推消除理由,提出了一种改善的非递归遍历算法并用C语言予以实现。
关键词:二叉树;遍历算法;非递归;C语言实现1009-3044(2014)01-0223-031 概述树形结构是一种非常常见的数据结构,而二叉树又是其中最重要的一种树形结构。
二叉树的遍历是指按照一定的规则和次序将二叉树中的每一个结点都访问一次,既不能重复,也不能漏掉。
一般而言,对二叉树的遍历有前序遍历、中序遍历、后序遍历和按层遍历等几种方式。
在具体的算法设计上,以上遍历方式一般采取递归算法来实现,该文将探讨采用非递归算法来实现二叉树的遍历。
2 二叉树的数据结构描述二叉树作为一种非线性结构,每个结点最多有一个双亲结点和两个子结点。
二叉树可以采用顺序存储结构和链式存储结构。
对于完全二叉树而言,采用顺序存储是非常方便并且节省空间的,但是对于大部分的非完全二叉树而言,采用顺序存储将导致空间浪费严重且结构混乱、效率低下。
因此,更多的时候,大家都更愿意用链式存储结构来表示二叉树,这样结构更加清晰,尤其是对于一种二叉树非递归遍历算法的C语言实现由写论文的好帮手.zbjy.提供,.左右子树的描述和双亲节点的描述更加方便。
该文中拟采用链式结构来表示二叉树。
用链式存储结构来表示二叉树,一个结点至少由3个域组成,即数据域、左子结点域和右子结点域(如图1所示)。
3 二叉树的遍历及递归算法实现3.1 二叉树的遍历二叉树的遍历就是一个不漏的访问树中的每个结点,同时也不能重复。
所谓“访问”,就是指对结点的数据域进行某种操作,比如说读取、删除、更新、求该节点深度等等。
对于二叉树中的任意一个部分,都可以把它看作三部分,根节点、左子树、右子树,我们用D表示访问跟结点,用L表示遍历左子树,用R表示遍历右子树,则共有以下6种遍历方式[1]。
二叉树的先序,中序,后序遍历的递归工作栈的关系在计算机科学中,二叉树是一种非常重要的数据结构,它在很多算法和数据处理中都有着广泛的应用。
而二叉树的先序、中序、后序遍历以及它们与递归和工作栈的关系更是程序员面试中常见的问题。
本文将从深度和广度两个方面,按照先序、中序、后序的顺序逐步展开对这个主题的探讨。
一、先序遍历先序遍历是指先访问根节点,然后递归地先序遍历左子树,最后递归地先序遍历右子树。
在实际的计算机算法中,我们可以使用递归或者栈来实现先序遍历。
1.1 递归实现当我们使用递归来实现先序遍历时,可以很容易地写出下面这段代码:```pythondef preorderTraversal(root):if not root:return []return [root.val] + preorderTraversal(root.left) + preorderTraversal(root.right)```这段代码非常简洁明了,但是在实际执行时,会使用工作栈来保存递归中间结果。
因为递归本质上就是一个栈结构,在调用递归函数时,会将当前函数的局部变量和参数压入栈中,直到递归结束,栈中的内容才会依次出栈执行。
1.2 栈实现除了递归之外,我们也可以使用显式栈来实现先序遍历。
这种方法通常会更加高效一些,因为递归会有一定的性能损耗。
栈的实现思路是,我们首先将根节点压入栈中,然后弹出栈顶节点并访问它,接着先将右子节点压入栈中,再将左子节点压入栈中。
重复上述操作直到栈为空。
这样就可以保证先访问根节点,再访问左子树,最后访问右子树,符合先序遍历的要求。
二、中序遍历中序遍历是指先递归地中序遍历左子树,然后访问根节点,最后递归地中序遍历右子树。
中序遍历同样可以用递归或者显式栈来实现。
2.1 递归实现递归实现中序遍历同样非常简单:```pythondef inorderTraversal(root):if not root:return []return inorderTraversal(root.left) + [root.val] + inorderTraversal(root.right)```在这个递归函数中,同样使用了递归的工作栈来保存中间结果。
三种遍历方法一、前序遍历前序遍历是二叉树遍历的一种方法,也是最常见的遍历方式之一。
在前序遍历中,首先访问根节点,然后递归地遍历左子树,最后递归地遍历右子树。
前序遍历的应用非常广泛,例如在二叉树的构建和重建、树的深度优先搜索等问题中都会用到前序遍历。
在进行前序遍历时,可以采用递归或者非递归的方式。
1. 递归实现前序遍历:递归实现前序遍历非常简单,具体步骤如下:- 首先判断当前节点是否为空,若为空则返回;- 访问当前节点;- 递归遍历左子树;- 递归遍历右子树。
2. 非递归实现前序遍历:非递归实现前序遍历需要借助栈来实现,具体步骤如下:- 将根节点入栈;- 循环执行以下步骤,直到栈为空:- 弹出栈顶节点,并访问该节点;- 若该节点的右子节点不为空,则将右子节点入栈;- 若该节点的左子节点不为空,则将左子节点入栈。
二、中序遍历中序遍历是二叉树遍历的另一种方法,同样也是一种常用的遍历方式。
在中序遍历中,首先递归地遍历左子树,然后访问根节点,最后递归地遍历右子树。
中序遍历的应用也非常广泛,例如在二叉搜索树的操作中,中序遍历可以按照升序输出所有节点的值。
1. 递归实现中序遍历:递归实现中序遍历的步骤如下:- 首先判断当前节点是否为空,若为空则返回;- 递归遍历左子树;- 访问当前节点;- 递归遍历右子树。
2. 非递归实现中序遍历:非递归实现中序遍历同样需要借助栈来实现,具体步骤如下:- 将根节点入栈;- 循环执行以下步骤,直到栈为空:- 若当前节点不为空,则将当前节点入栈,并将当前节点指向其左子节点;- 若当前节点为空,则弹出栈顶节点,并访问该节点,然后将当前节点指向其右子节点。
三、后序遍历后序遍历是二叉树遍历的另一种方式,也是最后一种常见的遍历方式。
在后序遍历中,首先递归地遍历左子树,然后递归地遍历右子树,最后访问根节点。
后序遍历的应用也非常广泛,例如在二叉树的删除操作中,需要先删除子节点,再删除根节点。