【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)
本文总结了刷LeetCode过程中,有关树的遍历的相关代码实现,包括了二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现。这也是解决树的遍历问题的固定套路。
一、二叉树的先序、中序、后序遍历
1、递归模板
(1)先序
1 public void preorder(TreeNode root) {
2 if (root == null) {
3 return;
4 }
5 res.add(root.val);
6 preorder(root.left);
7 preorder(root.right);
8 }
(2)中序
1 public void inorder(TreeNode root) {
2 if (root == null) {
3 return;
4 }
5 inorder(root.left);
6 res.add(root.val);
7 inorder(root.right);
8 }
(3)后序
1 public void postorder(TreeNode root) {
2 if (root == null) {
3 return;
4 }
5 postorder(root.left);
6 postorder(root.right);
7 res.add(root.val);
8 }
2、迭代模板:显式使用栈
(1)先序 。也可以使用后文的DFS实现
1 public void preorder(TreeNode root) {
2 Deque<TreeNode> stack = new ArrayDeque<>();
3 while(root !=null || !stack.isEmpty()) {
4 while(root != null) {
5 stack.push(root);
6 res.add(root.val);//保存结果
7 root = root.left;
8 }
9 root = stack.pop();
10 root = root.right;
11 }
12 }
(2)中序
1 public void inorder(TreeNode root) {
2 Deque<TreeNode> stack = new ArrayDeque<>();
3 while(root != null || !stack.isEmpty()) {
4 while(root != null) {
5 stack.push(root);
6 root = root.left;
7 }
8 root = stack.pop();
9 res.add(root.val);//保存结果
10 root = root.right;
11 }
12 }
(3)后序。先序是根——左——右,而后序是左-右-根,可以将先序改成根-右-左,然后将结果反转。如下代码对比先序的代码:
1 public void postorder(TreeNode root) {
2 Deque<TreeNode> stack = new ArrayDeque<>();
3 while(root != null || !stack.isEmpty()) {
4 while(root != null) {
5 stack.push(root);
6 res.add(root.val);//保存结果
7 root = root.right;//注意这里和先序、中序的差别
8 }
9 root = stack.pop();
10 root = root.left();
11 }
12 Collections.reverse(res);//将前面的结果反转
13 }
当然,上面这种方法比较间接,借助于先序遍历的理解。下面采用一种直接的迭代方式:
1 public void postorder(TreeNode root) {
2 Deque<TreeNode> stack = new ArrayDeque<>();
3 TreeNode preNode = new TreeNode();//该节点用于保存前一个出栈的节点
4 while (root != null || !stack.isEmpty()) {
5 //将当前节点的左子树节点一次入栈
6 while (root != null) {
7 stack.push(root);
8 root = root.left;
9 }
10 root = stack.peek();
11 //当前节点没有右孩子了,或者其右孩子已经出栈了,则当前节点也出栈
12 if (root.right == null || root.right == preNode) {
13 root = stack.pop();
14 res.add(root.val);//保存结果
15 preNode = root; //记录本次刚输出的节点
16 root = null;
17 } else {
18 //当前节点还有右孩子,且其右孩子还没有出栈,则先处理其右孩子
19 root = root.right;
20 }
21 }
22 }
二、N叉树的先序与后序遍历
1、递归模板
(1)先序
1 public void preorder(TreeNode root) {
2 if (root == null) {
3 return;
4 }
5 res.add(root.val);//保存结果
6 if (root.children != null) {
7 for (int i = 0; i < root.children.size(); i++) {
8 dfs(root.children.get(i));
9 }
10 }
11 }
(2)后序
1 public void postorder(TreeNode root) {
2 if (root == null) {
3 return;
4 }
5 for (int i = 0; i < root.children.size(); i++) {
6 postorder(root.children.get(i));
7 }
8 res.add(root.val);//保存结果
9 }
2、迭代模板
(1)先序。即下文中DFS的实现
1 public void preorder(Node root) {
2 Deque<Node> stack = new ArrayDeque<>(); //BFS使用队列,这里使用栈
3 stack.push(root);
4 while (!stack.isEmpty()) {
5 root = stack.pop();
6 res.add(root.val);//保存结果
7 int childCount = root.children == null ? 0 : root.children.size();
8 //这里需要注意孩子节点入栈的顺序
9 for (int i = childCount - 1; i >= 0; i--) {
10 stack.push(root.children.get(i));
11 }
12 }
13 }
(2)后序。先序是根——左——右,而后序是左-右-根,可以将先序改成根-右-左,然后将结果反转。如下代码对比先序的代码:
1 public void postorder(Node root) {
2 List<Integer> result = new ArrayList<>();
3 Deque<Node> stack = new ArrayDeque<>();
4 stack.push(root);
5 while (!stack.isEmpty()) {
6 root = stack.pop();
7 result.add(root.val);//保存结果
8 int childCount = root.children == null ? 0 : root.children.size();
9 //还入栈的顺序正好和先序遍历相反
10 for (int i = 0; i < childCount; i++) {
11 stack.push(root.children.get(i));
12 }
13 }
14 //将结果反转
15 Collections.reverse(result);
16 for (Integer i:result) {
17 System.out.print(i);
18 }
19 }
目前还没有找到类似二叉树直接后序遍历的方法
三、树的BFS(广度优先搜索)和DFS(深度优先搜索)实现模板
1、递归实现
(1)BFS
一般来说不能使用递归来实现BFS,因为BFS使用的时队列实现,而递归使用的是栈实现。
(2)DFS
普通的N叉树的DFS包括先序遍历和后序遍历,它们的递归实现和前文一致。如果是二叉树,还有中序遍历,递归实现和前文一致。
2、迭代实现
(1)BFS。即按层次来遍历
1 public void bfs(Node root) {
2 Queue<Node> queue = new LinkedList<>();
3 queue.offer(root);
4 while (!queue.isEmpty()) {
5 root = queue.poll();
6 res.add(root.val);//保存结果
7 if (root.children != null) {
8 queue.addAll(root.children);
9 }
10 }
11 }
(2)DFS
先序遍历、中序遍历(二叉树)和后序遍历的迭代方式实现和前文一致。
四、图的BFS和DFS遍历模板
树是图的一种特殊情况,相比于树而言图中可能出现环,所以在遍历的时候可能重复访问。所以树的BFS和DFS实现需要在树的基础上维护一个Set来避免访问重复的节点即可。
1、BFS
1 public void graphyBfs(Node root) {
2 if (root == null) {
3 return;
4 }
5 Set<Node> visitedSet = new HashSet<>(); //维护一个set,保存已经访问过的节点
6 Queue<Node> queue = new LinkedList<>();
7 queue.offer(root);
8 while (!queue.isEmpty()) {
9 root = queue.poll();
10 visitedSet.add(root);//保存每个已经输出的节点
11 res.add(root.val);//保存结果
12 if (root.children == null) {
13 return;
14 }
15 for (int i = 0; i < root.children.size(); i++) {
16 Node tmpNode = root.children.get(i);
17 if (!visitedSet.contains(tmpNode)) {//已经输出过的节点,则不用再加入
18 queue.offer(tmpNode);
19 }
20 }
21 }
22 }
2、DFS
迭代方式、递归方式,都维护一个set来保存已经访问过的节点,在输出节点时保存到set中,在添加节点时添加去重操作即可。
【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)的更多相关文章
- LeetCode:二叉树的前、中、后序遍历
描述: ------------------------------------------------------- 前序遍历: Given a binary tree, return the pr ...
- TZOJ 3209 后序遍历(已知中序前序求后序)
描述 在数据结构中,遍历是二叉树最重要的操作之一.所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问. 这里给出三种遍历算法. 1.中序遍历的递归算法定义: ...
- [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)
Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...
- [Java]算术表达式求值之一(中序表达式转后序表达式方案)
第二版请见:https://www.cnblogs.com/xiandedanteng/p/11451359.html 入口类,这个类的主要用途是粗筛用户输入的算术表达式: package com.h ...
- DS Tree 已知先序、中序 => 建树 => 求后序
参考:二叉树--前序和中序得到后序 思路历程: 在最初敲的时候,经常会弄混preorder和midorder的元素位置.大体的思路就是在preorder中找到根节点(根节点在序列的左边),然后在mid ...
- 已知树的前序、中序,求后序的java实现&已知树的后序、中序,求前序的java实现
public class Order { int findPosInInOrder(String str,String in,int position){ char c = str.charAt(po ...
- 分别求二叉树前、中、后序的第k个节点
一.求二叉树的前序遍历中的第k个节点 //求先序遍历中的第k个节点的值 ; elemType preNode(BTNode *root,int k){ if(root==NULL) return ' ...
- HDU 1710 (二叉树的前序和中序,求后序)
题目链接 题目大意: 输入二叉树的前序.中序遍历,请输出它的后序遍历 #include <stdio.h> #include <string.h> ; // 长度为n s1 前 ...
- hdu1710-Binary Tree Traversals (由二叉树的先序序列和中序序列求后序序列)
http://acm.hdu.edu.cn/showproblem.php?pid=1710 Binary Tree Traversals Time Limit: 1000/1000 MS (Java ...
随机推荐
- 超详细的 Vagrant 上手指南
搭建 Linux 虚拟机,别再用 VirtualBox 从 .iso 文件安装了. 概述 2020 年了,也许你已经习惯了 docker,习惯了在 XX 云上快速创建云主机,但是如果你想在个人电脑上安 ...
- springboot实现防重复提交和防重复点击
背景 同一条数据被用户点击了多次,导致数据冗余,需要防止弱网络等环境下的重复点击 目标 通过在指定的接口处添加注解,实现根据指定的接口参数来防重复点击 说明 这里的重复点击是指在指定的时间段内多次点击 ...
- 小白也能看懂的Redis教学基础篇——朋友面试被Skiplist跳跃表拦住了
各位看官大大们,双节快乐 !!! 这是本系列博客的第二篇,主要讲的是Redis基础数据结构中ZSet(有序集合)底层实现之一的Skiplist跳跃表. 不知道那些是Redis基础数据结构的看官们,可以 ...
- js简单数据类型和复杂数据类型
var timer = null; //简单数据类型null 返回的是一个空的对象 object console.log(typeof timer); 1.简单数据类型 在内存中存放在栈中,在里面开 ...
- JS寄快递地址智能解析
JS寄快递地址智能解析--2020年7月15日 去年做了些前端内容,最近在整理一些稍微有点用的内容,比如智能解析地址,用户只要输入:张三1351111111江苏省扬州市广陵区XX小区X楼xxx室,就能 ...
- 在.NET中使用DiagnosticSource
前言 DiagnosticSource是一个非常有意思的且非常有用的API,对于这些API它们允许不同的库发送命名事件,并且它们也允许应用程序订阅这些事件并处理它们,它使我们的消费者可以在运行时动态发 ...
- [iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ格式分析
[iTyran原创]iPhone中OpenGL ES显示3DS MAX模型之一:OBJ文件格式分析作者:yuezang - iTyran 在iOS的3D开发中常常需要导入通过3DS MAX之类 ...
- 002 01 Android 零基础入门 01 Java基础语法 01 Java初识 02 Java简介
002 01 Android 零基础入门 01 Java基础语法 01 Java初识 02 Java简介 学习Java的基础语法 Java是一门编程语言,学习的逻辑其实和现实世界的语言是一样的,需要了 ...
- #ifdef _DEBUG/ #define new DEBUG_NEW/ #endif的作用
转载:https://blog.csdn.net/minghui_/article/details/80748142 转自:#ifdef _DEBUG #define new DEBUG_NEW #e ...
- IDEA设置External Tools之Javap反编译字节码
通过Jdk的命令javap可以反编译查看字节码,但是在使用idea的时候一直用命令行去操作不太好操作,而且因为idea会把class码 放在target里面,经常会忘记切换目录.这个时候idea的Ex ...