介绍

深度优先遍历:从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止。然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点为止。

广度优先遍历:从根节点出发,在横向遍历二叉树层段节点的基础上纵向遍历二叉树的层次。

DFS实现:

数据结构:栈

父节点入栈,父节点出栈,先右子节点入栈,后左子节点入栈。递归遍历全部节点即可

BFS实现:

数据结构:队列

父节点入队,父节点出队列,先左子节点入队,后右子节点入队。递归遍历全部节点即可

树的实现


public class TreeNode<V> { private V value;
private List<TreeNode<V>> childList;//子节点列表 public TreeNode(V value) {
this.value = value;
} public TreeNode(V value, List<TreeNode<V>> childList) {
this.value = value;
this.childList = childList;
} public V getValue() {
return value;
} public void setValue(V value) {
this.value = value;
} public List<TreeNode<V>> getChildList() {
return childList;
} public void setChildList(List<TreeNode<V>> childList) {
this.childList = childList;
}
}

深度优先搜索算法(DFS)

深度优先搜索算法是指沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。

递归实现


public static <V> void dfs(TreeNode<V> tree, int depth) {
if (tree != null) {
//打印节点值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
for (TreeNode<V> item : tree.getChildList()) {
dfs(item, depth + 1);
}
}
}
}

非递归实现


public static <V> void dfsNotRecursive(TreeNode<V> tree) {
if (tree != null) {
//次数之所以用 Map 只是为了保存节点的深度,
//如果没有这个需求可以改为 Stack<TreeNode<V>>
Stack<Map<TreeNode<V>, Integer>> stack = new Stack<>();
Map<TreeNode<V>, Integer> root = new HashMap<>();
root.put(tree, 0);
stack.push(root);
while (!stack.isEmpty()) {
Map<TreeNode<V>, Integer> item = stack.pop();
TreeNode<V> node = item.keySet().iterator().next();
int depth = item.get(node);
//打印节点值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
if (node.getChildList() != null && !node.getChildList().isEmpty()) {
for (TreeNode<V> treeNode : node.getChildList()) {
Map<TreeNode<V>, Integer> map = new HashMap<>();
map.put(treeNode, depth + 1);
stack.push(map);
}
}
}
}
}

分类

一般来说 DFS 算法又分为如下三种:

1.前序遍历(Pre-Order Traversal) :指先访问根,然后访问子树的遍历方式


private static <V> void dfs(TreeNode<V> tree, int depth) {
if (d != null) {
//打印节点值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
for (TreeNode<V> item : tree.getChildList()) {
dfs(item, depth + 1);
}
}
}
}

2.后序遍历(Post-Order Traversal):指先访问子树,然后访问根的遍历方式


private static <V> void dfs(TreeNode<V> tree, int depth) {
if (d != null) {
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
for (TreeNode<V> item : tree.getChildList()) {
dfs(item, depth + 1);
}
}
//打印节点值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
}
}

3.中序遍历(In-Order Traversal):指先访问左(右)子树,然后访问根,最后访问右(左)子树的遍历方式。

中序遍历一般是用二叉树实现:


private static <V> void dfs(TreeNode<V> root, int depth) {
if (root.getLeft() != null){
dfs(root.getLeft(), depth + 1);
}
if (root.getRight() != null){
dfs(root.getRight(), depth + 1);
}
//打印节点值以及深度
System.out.println(d.getValue().toString() + ", " + depth);
}

广度优先搜索算法(Breadth-First Search,BFS)

广度优先搜索算法是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。

递归实现


public static <V> void bfs(List<TreeNode<V>> children, int depth) {
List<TreeNode<V>> thisChildren, allChildren = new ArrayList<>();
for (TreeNode<V> child: children) {
//打印节点值以及深度
System.out.println(child.getValue().toString() + ", " + depth);
thisChildren = child.getChildList();
if (thisChildren != null && thisChildren.size() > 0) {
allChildren.addAll(thisChildren);
}
}
if (allChildren.size() > 0) {
bfs(allChildren, depth + 1);
}
}

递归实现的方式我自己想了好久没想出来,最后还是在网上搜到的算法。

可以看到非递归实现有个问题就是无法遍历根节点,不过问题不大,而且我也还没想出来其他更优雅的办法来实现。

非递归实现


public static <V> void bfsNotRecursive(TreeNode<V> tree) {
if (tree != null) {
//跟上面一样,使用 Map 也只是为了保存树的深度,没这个需要可以不用 Map
Queue<Map<TreeNode<V>, Integer>> queue = new ArrayDeque<>();
Map<TreeNode<V>, Integer> root = new HashMap<>();
root.put(tree, 0);
queue.offer(root);
while (!queue.isEmpty()) {
Map<TreeNode<V>, Integer> itemMap = queue.poll();
TreeNode<V> itemTreeNode = itemMap.keySet().iterator().next();
int depth = itemMap.get(itemTreeNode);
//打印节点值以及深度
System.out.println(itemTreeNode.getValue().toString() + ", " + depth);
if (itemTreeNode.getChildList() != null &&
!itemTreeNode.getChildList().isEmpty()) {
for (TreeNode<V> child : itemTreeNode.getChildList()) {
Map<TreeNode<V>, Integer> map = new HashMap<>();
map.put(child, depth + 1);
queue.offer(map);
}
}
}
}
}

java二叉树遍历——深度优先(DFS)与广度优先(BFS) 递归版与非递归版的更多相关文章

  1. 【Python算法】遍历(Traversal)、深度优先(DFS)、广度优先(BFS)

    图结构: 非常强大的结构化思维(或数学)模型.如果您能用图的处理方式来规范化某个问题,即使这个问题本身看上去并不像个图问题,也能使您离解决问题更进一步. 在众多图算法中,我们常会用到一种非常实用的思维 ...

  2. JAVA 遍历文件夹下的所有文件(递归调用和非递归调用)

    JAVA 遍历文件夹下的所有文件(递归调用和非递归调用) 1.不使用递归的方法调用. public void traverseFolder1(String path) { int fileNum = ...

  3. [复习] JAVA 遍历目录 (递归调用和非递归)

    JAVA 遍历文件夹下的所有文件(递归调用和非递归调用) 1.不使用递归的方法调用. public void traverseFolder1(String path) { int fileNum = ...

  4. 二叉树中序遍历,先序遍历,后序遍历(递归栈,非递归栈,Morris Traversal)

    例题 中序遍历94. Binary Tree Inorder Traversal 先序遍历144. Binary Tree Preorder Traversal 后序遍历145. Binary Tre ...

  5. 左神算法基础班4_1&2实现二叉树的先序、中序、后序遍历,包括递归方式和非递归

    Problem: 实现二叉树的先序.中序.后序遍历,包括递归方式和非递归方式 Solution: 切记递归规则: 先遍历根节点,然后是左孩子,右孩子, 根据不同的打印位置来确定中序.前序.后续遍历. ...

  6. 深度优先dfs与广度bfs优先搜索总结+例题

    DFS(Deep First Search)深度优先搜索 深度优先遍历(dfs)是对一个连通图进行遍历的算法.它的思想是从一个顶点开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节 ...

  7. java 二叉树遍历

    package com.lever; import java.util.LinkedList;import java.util.Queue; /** * 二叉树遍历 * @author lckxxy ...

  8. Java 二叉树遍历相关操作

    BST二叉搜索树节点定义: /** * BST树的节点类型 * @param <T> */ class BSTNode<T extends Comparable<T>&g ...

  9. Java 二叉树遍历右视图-LeetCode199

    题目如下: 题目给出的例子不太好,容易让人误解成不断顺着右节点访问就好了,但是题目意思并不是这样. 换成通俗的意思:按层遍历二叉树,输出每层的最右端结点. 这就明白时一道二叉树层序遍历的问题,用一个队 ...

随机推荐

  1. Keepalived+LVS实现LNMP网站的高可用部署

    Keepalived+LVS实现LNMP网站的高可用部署   项目需求   当我们访问某个网站的时候可以在浏览器中输入IP或者域名链接到Web Server进行访问,如果这个Web Server挂了, ...

  2. 图解算法——链表中倒数第k个节点

    题目来源: 剑指 Offer 22. 链表中倒数第k个节点 leetCode 题目描述: 输入一个链表,输出该链表中倒数第k个节点.为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个 ...

  3. redis字典

    字典作为一种保存键值对的数据结构,在redis中使用十分广泛,redis作为数据库本身底层就是通过字典实现的,对redis的增删改查实际上也是构建在字典之上. 一.字典的结构

  4. MarkDown(文本编译器)

    MarkDown(一种高效的文本编译器) 推荐使用Typora 点击此处下载 使用方法 1. 首先创建一个文本文件xxx.txt. 2. 然后修改文件后缀为xxx.md.(可以记做玛德...) 3. ...

  5. USB2.0协议学习笔记---USB数据包结构

    USB包类型和传输过程  USB是一种串行总线,因此数据都是一位一位传输的,如同串口那样,但是USB在真实物理电路上却不是TTL电平,而是一种差分信号采用NRZI编码,就是用变化表示0,不变表示1,同 ...

  6. .bashrc & rename hostname & rename username

    .bashrc & rename hostname & rename username mbp $ pwd $ cd ~ $ pwd $ cat .bashrc $ vim .bash ...

  7. Bastion Host (BH)

    Bastion Host (BH) 堡垒机 堡垒主机是专门设计和构造成承受攻击网络上的专用计算机. 该计算机通常承载单个应用程序,例如代理服务器,并且所有其他服务都将被删除或限制以减少对计算机的威胁. ...

  8. React & update state with props & Object.assign

    React & update state with props & Object.assign Object.assign({}, oldObj, newObj) https://re ...

  9. js navigator.wakeLock 保持屏幕唤醒状态

    let lock; btn.addEventListener("click", async () => { try { if (lock) { lock.release(); ...

  10. 链表、栈、队列、KMP相关知识点

    链表.栈与队列.kmp; 数组模拟单链表: 用的最多的是邻接表--就是多个单链表: 作用:存储树与图 需要明确相关定义: 为什么需要使用数组模拟链表 比使用结构体 或者类来说 速度更快 代码简洁 算法 ...