图解二叉树非递归版的前序遍历算法
“ 图解用栈数据结构对树的前序遍历,中序遍历,后续遍历。”
树的遍历
所谓遍历 (Traversal) 是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题。
二叉树遍历
本文以二叉树的遍历为例总结。
二叉树由根结点及左、右子树这三个基本部分组成。因此,在任一给定结点上,可以按某种次序执行三个操作:
访问结点本身(Node)
遍历该结点的左子树(Left subtree)
遍历该结点的右子树(Right subtree)
根据访问结点操作发生位置不同,命名:
前序遍历 Preorder Traversal :访问根结点的操作发生在遍历其左右子树之前。
中序遍历 Inorder Traversal:访问根结点的操作发生在遍历其左右子树之中间。
后序遍历 Postorder Traversal : 访问根结点的操作发生在遍历其左右子树之后。
二叉树的数据结构定义如下所示:
public class TreeNode<T>{public T val { get; set; }public TreeNode<T> left { get; set; }public TreeNode<T> right { get; set; }public TreeNode(T data){val = data;}}
思考算法
我们借助栈,先将根节点推入栈内,因为首先访问根节点,这意味着它必须先出栈,但是我们还没有机会让左右子树入栈呢?
自然地,我们想到定义一个临时变量引用根节点,这样在循环时,不断修改这个临时变量,我们在此处将它成为上下文 context,它始终指向当前遍历子树的根节点,如下图所示,假设为某个遍历时的子树,此时context指向的便是子树的根节点,节点1出栈后,通过context引用它,然后找到它的左右子树,即 context.left, context.right。
想明白这个context的作用,栈s的出栈时机,进栈时机在前序遍历不难理解,所以循环体内完成一个完整的一步做的事情也就容易理解了,一个遍历步骤做的事情如下:
- context指向的当前子树的根节点为null吗? 如果是,则将context指向栈 s的栈顶,即下一个子树根节点,实际上为右子树。
- 访问栈顶,并出栈栈顶元素,出栈了不用担心,我们通过context引用了这个出栈元素。
- context.right 进栈
- context.left 进栈,至于为什么是先进栈right,这里不解释了。
- 既然访问完了子树的根节点,那么前序遍历下一步是访问左子树了,自然地,context被迭代为context.left 。
前序遍历(栈结构实现)
前序遍历 Preorder Traversal :先访问根结点,然后访问根节点的左子树,最后访问根节点的右子树。
给定如下图所示的二叉树,
那么用栈这种数据结构,如何前序遍历这颗二叉树呢?
首先,我们把代码放在这里,然后分析这个代码是怎么想出来的。
public static IList<T> PreorderTraversal<T>(TreeNode<T> root){IList<T> rtn = new List<T>();if (root == null) return rtn;var s = new Stack<TreeNode<T>>();s.Push(root);TreeNode<T> context = root;while (s.Count > 0) { if (context == null)context = s.Peek();rtn.Add(s.Peek().val); //访问栈顶元素s.Pop();if (context.right != null) //左子树入栈s.Push(context.right);if (context.left != null) //右子树入栈s.Push(context.left);context = context.left;}return rtn;}
while遍历前,各个变量的状态
s 栈有一个节点 1
context 上下文变量引用节点 1
第一次遍历,此时栈中含有元素,进入循环体内,上下文为节点 1,不满足 if 条件,访问 节点 1,节点 1 出栈, 节点 1 的右子树不为 null 先入栈,左子树也为 null后入栈, 上下文变量赋值为 左子树节点 2,如下图所示:
context = node2
第二次遍历,访问节点 2,然后节点 2 出栈,节点 2 的右子树不为 null,入栈,此时的 context 被赋值为节点 2的左子树,即为 null
context = null
第三次遍历,会特殊一些了,因为此时的context为 null,注意源码实现中的 if 条件,
if (context == null)
context = s.Peek();
满足了条件,context 被赋值为栈顶节点 4,然后执行逻辑与以上遍历相同,访问栈顶节点 4,显然这个节点 4的左右子树皆为 null,context再次被赋值,且等于 null。
context = null
第四次遍历,依然满足 if 条件,context 被赋值为栈顶节点 3,执行逻辑与上相似,访问栈顶节点 3,节点 3 出栈,节点 3 的右子树为null,左子树不为null,入栈,context再次被赋值为节点 3 的左子树 5
context = node5
第五次遍历,不满足 if 条件了,直接访问节点 5, 然后节点 5 出栈,显然节点 5 左右子树为null,context赋值为node5.left,即为null,并且栈的元素已经空了。
context = null
此时,while (s.Count > 0) 不满足了,退出遍历。
前序遍历总结
前序遍历在遍历前先入栈根节点,context引用根节点,进入遍历后,有一个 if 条件判断context为null吗,如果是说明上文的左子树为null,context需要引用此时栈顶元素,访问栈顶元素,栈顶元素出栈,接下来,首先入栈右子树,然后入栈左子树,context为 context.left 进行迭代。因此,以上二叉树访问节点的次序为
1->2->4->3->5
中序遍历
中序遍历,遍历根节点位于遍历左子树和右子树之间,即左子树,根节点,右子树。
中序遍历的源码如下:
public IList<T> InorderTraversal<T>(TreeNode<T> root){IList<T> rtn = new List<T>();var s = new Stack<TreeNode<T>>();if (root == null) return rtn;s.Push(root);while (s.Count > 0){var context = s.Peek();while (context != null) {s.Push(context.left);context = context.left;}s.Pop();if (s.Count == 0)return rtn;rtn.Add(s.Peek().val); TreeNode<T> curNode = s.Pop();s.Push(curNode.right);}return rtn;}
不妨您先思考一下,这段代码是怎么构思出来的,分析中序遍历,后续遍历图解,敬请关注明天的推送,谢谢。
本文由算法实验室倾情奉献,除合作社区及合作媒体外,禁止转载。
算法实验室分享最新的算法和应用实现相关技术内容。
图解二叉树非递归版的前序遍历算法相关推荐
- 图解二叉树非递归版的中序遍历算法
你会学到什么 讨论的问题是什么 这个问题相关的概念和理论 非递归版中序遍历算法 代码思考 算法技巧 实现代码 快照 评价算法 总结 欢迎关注算法思考与应用公众号 你会学到什么? 树的递归遍历算法很容易 ...
- C++版二叉树非递归遍历
C++版二叉树非递归遍历 文章目录 C++版二叉树非递归遍历 一.二叉树前序遍历 二.二叉树中序遍历 三.二叉树后序遍历 一.二叉树前序遍历 /*** Definition for a binary ...
- 二叉树的层序遍历,前序遍历(递归,非递归),中序遍历(递归,非递归),后续遍历(递归,非递归)
文章目录 二叉树的层序遍历 前序遍历 递归版本 非递归版本 中序遍历 递归版本 非递归版本 后序遍历 递归版本 非递归版本 二叉树的层序遍历 void printTree(BinaryTree* ar ...
- 二叉树非递归中序遍历
二叉树的中序遍历 为什么把中序遍历放在最前面呢,因为在非递归遍历中,这个是最简单也是最容易理解的,所以放在第一个的位置. 中序遍历的递归算法很简单,但是想要非递归的实现,就要用到栈这个数据结构, 那么 ...
- 二叉树非递归遍历的经典求解
#include <stdio.h> #include <stdlib.h> typedef int datatype; typedef struct node {dataty ...
- 二叉树的遍历:先序 中序 后序遍历的递归与非递归实现及层序遍历
二叉树的定义:一种基本的数据结构,是一种每个节点的儿子数目都不多于2的树 树节点的定义如下: // 树(节点)定义 struct TreeNode {int data; // 值TreeNode* l ...
- 二叉树前中后序遍历的非递归实现以及层次遍历、zig-zag型遍历详解
前言 二叉树的遍历是一个比较常见的问题,递归实现二叉树的前中后序遍历比较简单,但非递归实现二叉树的前中后序遍历相对有难度.这篇博客将详述如何使用非递归的方式实现二叉树的前中后序遍历,在进行理论描述的同 ...
- python实现二叉树非递归前中后序遍历
python实现二叉树非递归前中后层序遍历 二叉树是数据结构中重要的一部分,本文简单介绍用python实现二叉树的前中后序遍历,包括递归和非递归思路算法. # -*- 二叉树 begin -*- # ...
- 一种二叉树非递归遍历的简单写法
一种二叉树非递归遍历的简单写法 目录 一种二叉树非递归遍历的简单写法 先序遍历 中序遍历 后序遍历 二叉树的遍历是数据结构中非常基础的一个知识点,也是面试手撕代码环节的一个常见题目.这个问题的递归写法 ...
最新文章
- RPC协议之争和选型要点
- java完整程序_求一个完整的java程序
- Python__数据结构与算法——查找与排序
- python里面返回上一步_Python中的这3个骚操作你会吗?
- 洛谷 1226 取余运算||快速幂
- 百度、WordPress纷纷宣布停用React
- 信息学奥赛一本通(1107:校门外的树)
- Java 面试之数据结构
- 支付宝沙箱环境下模拟下单流程
- mysql 表自动复制_mysql-10临时表、复制表
- 易华录数据湖事业部安全中心招聘中高级安全工程师2-4人
- 关于eclipse adt更新的问题
- c语言中node是数据类型吗,lnode(数据结构lnode是什么类型)
- CleanMyMac X试用版与正式版区别
- 独家深挖!F1赛车协会“刹车表现”是如何进行数据分析的?
- 如何安装或卸载Google Chrome浏览器
- 给不会打字的朋友推荐一种鼠标写字的输入法
- MindMaster 快捷键
- CentOS基础系列五 :搭建FTP服务(详细图解)
- freemap初学者教程_Jupyter初学者笔记本:教程
热门文章
- CSS实现鼠标悬浮时背景图片拉近且增加遮罩层效果
- html5使用canvas实现小球碰撞反弹实例
- [转]Pico Neo 2✨五、实现Pico到电脑的投屏
- tcgames使用有延迟_教你用tcgames电脑玩刺激战场匹配手机的正确姿势:如何降低延迟卡顿...
- Method has to have one of the following return types
- 平车调整刀片如何调整_电脑平车剪不断线怎么调
- 不用对 雷蛇影鲛终极版thx 耳机开启空间音效。
- mysql 自动关闭服务_mysql自动关闭服务、连接限制等问题的解决方法
- 通道——旧照片——鼓浪屿旧巷
- 1.7.2服务器修改器,关于S7修改器V1.72的问题