二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)
二叉树是一种非常重要的数据结构,非常多其他数据结构都是基于二叉树的基础演变而来的。对于二叉树,有深度遍历和广度遍历,深度遍历有前序、中序以及后序三种遍历方法,广度遍历即我们寻常所说的层次遍历。由于树的定义本身就是递归定义,因此採用递归的方法去实现树的三种遍历不仅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。第一部分即直接訪问之,之后在推断左子树是否为空,不为空时即反复上面的步骤,直到其为空。若为空。则须要訪问右子树。注意。在訪问过左孩子之后。须要反过来訪问其右孩子。所以,须要栈这样的数据结构的支持。对于随意一个结点node,详细过程例如以下:
a)訪问之,并把结点node入栈。当前结点置为左孩子;
b)推断结点node是否为空,若为空。则取出栈顶结点并出栈,将右孩子置为当前结点;否则反复a)步直到当前结点为空或者栈为空(能够发现栈中的结点就是为了訪问右孩子才存储的)
代码例如以下:
- public void preOrderTraverse2(TreeNode root) {
- LinkedList<TreeNode> stack = new LinkedList<>();
- TreeNode pNode = root;
- while (pNode != null || !stack.isEmpty()) {
- if (pNode != null) {
- System.out.print(pNode.val+" ");
- stack.push(pNode);
- pNode = pNode.left;
- } else { //pNode == null && !stack.isEmpty()
- TreeNode node = stack.pop();
- pNode = node.right;
- }
- }
- }
二、中序遍历
1)依据上文提到的遍历思路:左子树 ---> 根结点 ---> 右子树,非常easy写出递归版本号:
- public void inOrderTraverse1(TreeNode root) {
- if (root != null) {
- inOrderTraverse1(root.left);
- System.out.print(root.val+" ");
- inOrderTraverse1(root.right);
- }
- }
2)非递归实现,有了上面前序的解释,中序也就比較简单了。同样的道理。仅仅只是訪问的顺序移到出栈时。代码例如以下:
- public void inOrderTraverse2(TreeNode root) {
- LinkedList<TreeNode> stack = new LinkedList<>();
- TreeNode pNode = root;
- while (pNode != null || !stack.isEmpty()) {
- if (pNode != null) {
- stack.push(pNode);
- pNode = pNode.left;
- } else { //pNode == null && !stack.isEmpty()
- TreeNode node = stack.pop();
- System.out.print(node.val+" ");
- pNode = node.right;
- }
- }
- }
三、后序遍历
1)依据上文提到的遍历思路:左子树 ---> 右子树 ---> 根结点。非常easy写出递归版本号:
- public void postOrderTraverse1(TreeNode root) {
- if (root != null) {
- postOrderTraverse1(root.left);
- postOrderTraverse1(root.right);
- System.out.print(root.val+" ");
- }
- }
2)
后序遍历的非递归实现是三种遍历方式中最难的一种。由于在后序遍历中,要保证左孩子和右孩子都已被訪问而且左孩子在右孩子前訪问才干訪问根结点,这就为流程的控制带来了难题。以下介绍两种思路。
第一种思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索。直到搜索到没有左孩子的结点,此时该结点出如今栈顶,可是此时不能将其出栈并訪问,因此其右孩子还为被訪问。
所以接下来依照同样的规则对其右子树进行同样的处理,当訪问完其右孩子时。该结点又出如今栈顶,此时能够将其出栈并訪问。这样就保证了正确的訪问顺序。能够看出,在这个过程中,每一个结点都两次出如今栈顶,仅仅有在第二次出如今栈顶时,才干訪问它。因此须要多设置一个变量标识该结点是否是第一次出如今栈顶。
void postOrder2(BinTree *root) //非递归后序遍历{ stack<BTNode*> s; BinTree *p=root; BTNode *temp;while(p!=NULL||!s.empty()) {while(p!=NULL) //沿左子树一直往下搜索。直至出现没有左子树的结点 { BTNode *btn=(BTNode *)malloc(sizeof(BTNode)); btn->btnode=p; btn->isFirst=true; s.push(btn); p=p->lchild; }if(!s.empty()) { temp=s.top(); s.pop();if(temp->isFirst==true) //表示是第一次出如今栈顶 { temp->isFirst=false; s.push(temp); p=temp->btnode->rchild; }else //第二次出如今栈顶 { cout<<temp->btnode->data<<" "; p=NULL; } } } }
另外一种思路:要保证根结点在左孩子和右孩子訪问之后才干訪问,因此对于任一结点P。先将其入栈。假设P不存在左孩子和右孩子。则能够直接訪问它;或者P存在左孩子或者右孩子。可是其左孩子和右孩子都已被訪问过了。则相同能够直接訪问该结点。若非上述两种情况。则将P的右孩子和左孩子依次入栈。这样就保证了每次取栈顶元素的时候,左孩子在右孩子前面被訪问。左孩子和右孩子都在根结点前面被訪问。
void postOrder3(BinTree *root) //非递归后序遍历{ stack<BinTree*> s; BinTree *cur; //当前结点 BinTree *pre=NULL; //前一次訪问的结点 s.push(root);while(!s.empty()) { cur=s.top();if((cur->lchild==NULL&&cur->rchild==NULL)|| (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild))) { cout<<cur->data<<" "; //假设当前结点没有孩子结点或者孩子节点都已被訪问过 s.pop(); pre=cur; }else {if(cur->rchild!=NULL) s.push(cur->rchild);if(cur->lchild!=NULL) s.push(cur->lchild); } } }
四、层次遍历
层次遍历的代码比較简单。仅仅须要一个队列就可以。先在队列中增加根结点。之后对于随意一个结点来说。在其出队列的时候,訪问之。同一时候假设左孩子和右孩子有不为空的。入队列。代码例如以下:
- public void levelTraverse(TreeNode root) {
- if (root == null) {
- return;
- }
- LinkedList<TreeNode> queue = new LinkedList<>();
- queue.offer(root);
- while (!queue.isEmpty()) {
- TreeNode node = queue.poll();
- System.out.print(node.val+" ");
- if (node.left != null) {
- queue.offer(node.left);
- }
- if (node.right != null) {
- queue.offer(node.right);
- }
- }
- }
五、深度优先遍历
事实上深度遍历就是上面的前序、中序和后序。可是为了保证与广度优先遍历相照顾,也写在这。代码也比較好理解,事实上就是前序遍历,代码例如以下:
- public void depthOrderTraverse(TreeNode root) {
- if (root == null) {
- return;
- }
- LinkedList<TreeNode> stack = new LinkedList<>();
- stack.push(root);
- while (!stack.isEmpty()) {
- TreeNode node = stack.pop();
- System.out.print(node.val+" ");
- if (node.right != null) {
- stack.push(node.right);
- }
- if (node.left != null) {
- stack.push(node.left);
- }
- }
- }
二叉树遍历(前序、中序、后序、层次、深度优先、广度优先遍历)相关推荐
- java 树 广度优先遍历_Java二进制搜索树遍历操作的详细描述[前,中,后,层次,广度优先遍历]...
本文介绍了Java二进制搜索树遍历操作. 与您分享以供参考,如下: 前言: 在Java Binary Search Tree Basics的上一节中,我们了解了该树及其相关知识,并对Binary Se ...
- java中二叉树_Java工程师面试1000题224-递归非递归实现二叉树前、中、后序遍历...
224.使用递归和非递归实现二叉树的前.中.后序遍历 使用递归来实现二叉树的前.中.后序遍历比较简单,直接给出代码,我们重点讨论非递归的实现. class Node { public int valu ...
- C++实现二叉树 前、中、后序遍历(递归与非递归)非递归实现过程最简洁版本
本文并非我所写,是复制的该链接中的内容: 最近学习二叉树,想编程实现递归和非递归的实现方式: 递归的方式就不说了,因为大家的递归程序都一样:但是对于非递归的实现方式, 根据这几天的查阅资料已看到差不多 ...
- 【LeetCode | 二叉树前、中、后序遍历{迭代法}实现】
1.前序遍历 // 解题思路:利用栈的原理实现以迭代方法来前序遍历(根左右)二叉树 class Solution { public:vector<int> preorderTraversa ...
- java数据结构学习笔记-二叉树前、中、后序遍历
public class BinaryTreeDemo {public static void main(String args[]){Employee emp1= new Employee(1,&q ...
- 【LeetCode | 二叉树前、中、后序遍历{递归法}实现】
1.前序遍历 #include <iostream> #include <vector> #include <queue> #include <algorit ...
- 二叉树前、中、后序线索化及遍历
public class ThreadedBinaryTree {public static void main(String[] args){Heronodes node1=new Heronode ...
- 二叉树的前、中、后、层次非递归遍历(js)
有如下二叉树 遍历: // 前序遍历, head-left-rightfunction HLR (tree) {const stack = [], res = []if (tree) stack.p ...
- 二叉树前序中序后续线索树_二叉树的先序,中序,后序遍历以及线索二叉树的遍历...
二叉树的先序,中序,后序遍历以及线索二叉树的遍历 (2008-05-04 17:52:49) 标签: 杂谈 C++ 二叉树的先序,中序,后序遍历以及线索二叉树的遍历 头文件 //*********** ...
- 二叉树前序遍历python输出_[宜配屋]听图阁 - Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例...
本文实例讲述了Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作.分享给大家供大家参考,具体如下: 实现一个功能: 输入:一颗二叉树的先序和中序遍历 输出:后续遍历 思想: 先序遍历中,第 ...
最新文章
- Windows7无法自动将IP协议堆栈绑定到网络适配器的办法
- sys连接oracle数据库提示无法解析,用Oracle数据库,用sys登录的时候口令正确,但是却登录不了...
- [攻防世界 pwn]——guess_num
- .Net Core Nuget还原失败
- 给用户权限数据添加缓存
- python考试编程题九道_一道逻辑推理题的程序实现(纯属娱乐)
- CNN结构基元:纹理结构和纹理基元方程化GLOH、Gabor...(Code)
- ApiController得到服务器端绝对路径
- 2018-2019-2 20165209 《网络对抗技术》Exp3:免杀原理与实践
- java servlet 学习_java学习之web基础(1):Servlet
- hibernate一对多双向关联中怎么配置list
- 另类的切图仔画图方案:svg编辑器+css
- 联想计算机怎么设置硬盘,计算机设置硬盘启动的具体方法_如何在联想计算机上设置硬盘启动...
- ACER-4738ZG 拆机改散热
- 深入理解 Base64 底层原理
- C++ vector数据合并去除重复项
- aop实现拦截保存操作日志
- linux qt make文件或目录,rpm,linux_为Qt程序制作rpm包的spec文件里的路径问题,rpm,linux,qt,rpmbuild,spec - phpStudy...
- 机器学习(二)-一元线性回归算法(代码实现及数学证明)
- 计算机和音乐结合的作品,用计算机创作多媒体作品──音乐和声音张燕.doc