|
|
|
@ -0,0 +1,498 @@
|
|
|
|
|
## 基本介绍
|
|
|
|
|
|
|
|
|
|
> 为什么要有图
|
|
|
|
|
|
|
|
|
|
线性表和树:
|
|
|
|
|
|
|
|
|
|
- 线性表局限于一个直接前驱和一个直接后继的关系
|
|
|
|
|
- 树也只能有一个直接前驱也就是父节点
|
|
|
|
|
- 当我们需要表示多对多的关系时, 这里我们就用到了图
|
|
|
|
|
|
|
|
|
|
> 图的举例说明
|
|
|
|
|
|
|
|
|
|
图是一种数据结构
|
|
|
|
|
|
|
|
|
|
- 其中结点可以具有零个或多个相邻元素
|
|
|
|
|
- 两个结点之间的连接称为边
|
|
|
|
|
- 结点也可以称为顶点
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 常用概念
|
|
|
|
|
|
|
|
|
|
- 顶点(vertex)
|
|
|
|
|
|
|
|
|
|
- 边(edge)
|
|
|
|
|
|
|
|
|
|
- 路径
|
|
|
|
|
- 路径: 比如从 D -> C 的路径有
|
|
|
|
|
1) D->B->C
|
|
|
|
|
2) D->A->B->C
|
|
|
|
|
|
|
|
|
|
- 无向图
|
|
|
|
|
|
|
|
|
|
- **无向图**: 顶点之间的连接没有方向,比如A-B,
|
|
|
|
|
|
|
|
|
|
即可以是 A -> B 也可以 B -> A
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
- 有向图
|
|
|
|
|
|
|
|
|
|
- **有向图**: 顶点之间的连接有方向,比如A-B,只能是 A-> B 不能是 B->A
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
- 带权图
|
|
|
|
|
|
|
|
|
|
- **带权图**:这种边带权值的图也叫网
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 图的表示方式
|
|
|
|
|
|
|
|
|
|
图的表示方式有两种:
|
|
|
|
|
|
|
|
|
|
- 二维数组表示(邻接矩阵)
|
|
|
|
|
- 链表表示(邻接表)
|
|
|
|
|
|
|
|
|
|
> 邻接矩阵
|
|
|
|
|
|
|
|
|
|
邻接矩阵是表示图形中顶点之间相邻关系的矩阵,对于n个顶点的图而言,矩阵是的row和col表示的是1....n个点
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
> 邻接表
|
|
|
|
|
|
|
|
|
|
- 邻接矩阵需要为每个顶点都分配n个边的空间,其实有很多边都是不存在,会造成空间的一定损失
|
|
|
|
|
- 邻接表的实现只关心存在的边,不关心不存在的边,因此没有空间浪费,邻接表由数组+链表组成
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 快速入门案例
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 遍历介绍
|
|
|
|
|
|
|
|
|
|
所谓图的遍历,即是对结点的访问
|
|
|
|
|
|
|
|
|
|
一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种访问策略:
|
|
|
|
|
|
|
|
|
|
- 深度优先遍历
|
|
|
|
|
- 广度优先遍历
|
|
|
|
|
|
|
|
|
|
### 深度优先遍历
|
|
|
|
|
|
|
|
|
|
> 图的深度优先搜索(Depth First Search)
|
|
|
|
|
|
|
|
|
|
- 深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解:**每次都在访问完当前结点后首先访问当前结点的第一个邻接结点**
|
|
|
|
|
- 我们可以看到,这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问
|
|
|
|
|
- 深度优先搜索是一个递归的过程
|
|
|
|
|
|
|
|
|
|
> 深度优先遍历算法步骤
|
|
|
|
|
|
|
|
|
|
1. 访问初始结点v,并标记结点v为已访问
|
|
|
|
|
2. 查找结点v的第一个邻接结点w
|
|
|
|
|
3. 若w存在,则继续执行4,如果w不存在,则回到第1步,将从v的下一个结点继续
|
|
|
|
|
4. 若w未被访问,对w进行深度优先遍历递归(即把w当做另一个v,然后进行步骤123)
|
|
|
|
|
5. 查找结点v的w邻接结点的下一个邻接结点,转到步骤3
|
|
|
|
|
|
|
|
|
|
### 深度优先搜索(DFS)
|
|
|
|
|
|
|
|
|
|
深度优先搜索(Depth-First Search / DFS)是一种**优先遍历子节点**而不是回溯的算法。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
**DFS解决的是连通性的问题**。即给定两个点,一个是起始点,一个是终点,判断是不是有一条路径能从起点连接到终点。起点和终点,也可以指的是某种起始状态和最终的状态。问题的要求并不在乎路径是长还是短,只在乎有还是没有。
|
|
|
|
|
|
|
|
|
|
#### 代码实现
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
import lombok.AllArgsConstructor;
|
|
|
|
|
import lombok.Data;
|
|
|
|
|
import lombok.NoArgsConstructor;
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Depth-First Search(DFS)
|
|
|
|
|
* <p>
|
|
|
|
|
* 从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止。然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点为止。
|
|
|
|
|
* <p>
|
|
|
|
|
* 数据结构:栈
|
|
|
|
|
* 父节点入栈,父节点出栈,先右子节点入栈,后左子节点入栈。递归遍历全部节点即可
|
|
|
|
|
*
|
|
|
|
|
* @author lry
|
|
|
|
|
*/
|
|
|
|
|
public class DepthFirstSearch {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 树节点
|
|
|
|
|
*
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
@Data
|
|
|
|
|
@NoArgsConstructor
|
|
|
|
|
@AllArgsConstructor
|
|
|
|
|
public static class TreeNode<V> {
|
|
|
|
|
private V value;
|
|
|
|
|
private List<TreeNode<V>> childList;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 二叉树节点支持如下
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public TreeNode<V> getLeft() {
|
|
|
|
|
if (childList == null || childList.isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return childList.get(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TreeNode<V> getRight() {
|
|
|
|
|
if (childList == null || childList.isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return childList.get(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 模型:
|
|
|
|
|
* .......A
|
|
|
|
|
* ...../ \
|
|
|
|
|
* ....B C
|
|
|
|
|
* .../ \ / \
|
|
|
|
|
* ..D E F G
|
|
|
|
|
* ./ \ / \
|
|
|
|
|
* H I J K
|
|
|
|
|
*/
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
TreeNode<String> treeNodeA = new TreeNode<>("A", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeB = new TreeNode<>("B", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeC = new TreeNode<>("C", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeD = new TreeNode<>("D", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeE = new TreeNode<>("E", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeF = new TreeNode<>("F", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeG = new TreeNode<>("G", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeH = new TreeNode<>("H", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeI = new TreeNode<>("I", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeJ = new TreeNode<>("J", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeK = new TreeNode<>("K", new ArrayList<>());
|
|
|
|
|
// A->B,C
|
|
|
|
|
treeNodeA.getChildList().add(treeNodeB);
|
|
|
|
|
treeNodeA.getChildList().add(treeNodeC);
|
|
|
|
|
// B->D,E
|
|
|
|
|
treeNodeB.getChildList().add(treeNodeD);
|
|
|
|
|
treeNodeB.getChildList().add(treeNodeE);
|
|
|
|
|
// C->F,G
|
|
|
|
|
treeNodeC.getChildList().add(treeNodeF);
|
|
|
|
|
treeNodeC.getChildList().add(treeNodeG);
|
|
|
|
|
// D->H,I
|
|
|
|
|
treeNodeD.getChildList().add(treeNodeH);
|
|
|
|
|
treeNodeD.getChildList().add(treeNodeI);
|
|
|
|
|
// G->J,K
|
|
|
|
|
treeNodeG.getChildList().add(treeNodeJ);
|
|
|
|
|
treeNodeG.getChildList().add(treeNodeK);
|
|
|
|
|
|
|
|
|
|
System.out.println("非递归方式");
|
|
|
|
|
dfsNotRecursive(treeNodeA);
|
|
|
|
|
System.out.println();
|
|
|
|
|
System.out.println("前续遍历");
|
|
|
|
|
dfsPreOrderTraversal(treeNodeA, 0);
|
|
|
|
|
System.out.println();
|
|
|
|
|
System.out.println("后续遍历");
|
|
|
|
|
dfsPostOrderTraversal(treeNodeA, 0);
|
|
|
|
|
System.out.println();
|
|
|
|
|
System.out.println("中续遍历");
|
|
|
|
|
dfsInOrderTraversal(treeNodeA, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 非递归方式
|
|
|
|
|
*
|
|
|
|
|
* @param tree
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
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.print("-->[" + node.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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 递归前序遍历方式
|
|
|
|
|
* <p>
|
|
|
|
|
* 前序遍历(Pre-Order Traversal) :指先访问根,然后访问子树的遍历方式,二叉树则为:根->左->右
|
|
|
|
|
*
|
|
|
|
|
* @param tree
|
|
|
|
|
* @param depth
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
public static <V> void dfsPreOrderTraversal(TreeNode<V> tree, int depth) {
|
|
|
|
|
if (tree != null) {
|
|
|
|
|
// 打印节点值以及深度
|
|
|
|
|
System.out.print("-->[" + tree.getValue().toString() + "," + depth + "]");
|
|
|
|
|
|
|
|
|
|
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
|
|
|
|
|
for (TreeNode<V> item : tree.getChildList()) {
|
|
|
|
|
dfsPreOrderTraversal(item, depth + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 递归后序遍历方式
|
|
|
|
|
* <p>
|
|
|
|
|
* 后序遍历(Post-Order Traversal):指先访问子树,然后访问根的遍历方式,二叉树则为:左->右->根
|
|
|
|
|
*
|
|
|
|
|
* @param tree
|
|
|
|
|
* @param depth
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
public static <V> void dfsPostOrderTraversal(TreeNode<V> tree, int depth) {
|
|
|
|
|
if (tree != null) {
|
|
|
|
|
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
|
|
|
|
|
for (TreeNode<V> item : tree.getChildList()) {
|
|
|
|
|
dfsPostOrderTraversal(item, depth + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 打印节点值以及深度
|
|
|
|
|
System.out.print("-->[" + tree.getValue().toString() + "," + depth + "]");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 递归中序遍历方式
|
|
|
|
|
* <p>
|
|
|
|
|
* 中序遍历(In-Order Traversal):指先访问左(右)子树,然后访问根,最后访问右(左)子树的遍历方式,二叉树则为:左->根->右
|
|
|
|
|
*
|
|
|
|
|
* @param tree
|
|
|
|
|
* @param depth
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
public static <V> void dfsInOrderTraversal(TreeNode<V> tree, int depth) {
|
|
|
|
|
if (tree.getLeft() != null) {
|
|
|
|
|
dfsInOrderTraversal(tree.getLeft(), depth + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 打印节点值以及深度
|
|
|
|
|
System.out.print("-->[" + tree.getValue().toString() + "," + depth + "]");
|
|
|
|
|
|
|
|
|
|
if (tree.getRight() != null) {
|
|
|
|
|
dfsInOrderTraversal(tree.getRight(), depth + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 输出结果
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
非递归方式
|
|
|
|
|
-->[A,0]-->[C,1]-->[G,2]-->[K,3]-->[J,3]-->[F,2]-->[B,1]-->[E,2]-->[D,2]-->[I,3]-->[H,3]
|
|
|
|
|
前续遍历
|
|
|
|
|
-->[A,0]-->[B,1]-->[D,2]-->[H,3]-->[I,3]-->[E,2]-->[C,1]-->[F,2]-->[G,2]-->[J,3]-->[K,3]
|
|
|
|
|
后续遍历
|
|
|
|
|
-->[H,3]-->[I,3]-->[D,2]-->[E,2]-->[B,1]-->[F,2]-->[J,3]-->[K,3]-->[G,2]-->[C,1]-->[A,0]
|
|
|
|
|
中续遍历
|
|
|
|
|
-->[H,3]-->[D,2]-->[I,3]-->[B,1]-->[E,2]-->[A,0]-->[F,2]-->[C,1]-->[J,3]-->[G,2]-->[K,3]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 广度优先遍历
|
|
|
|
|
|
|
|
|
|
> 图的广度优先搜索(Broad First Search)
|
|
|
|
|
|
|
|
|
|
类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来访问这些结点的邻接结点
|
|
|
|
|
|
|
|
|
|
> 广度优先遍历算法步骤
|
|
|
|
|
|
|
|
|
|
1. 访问初始结点v并标记结点v为已访问
|
|
|
|
|
2. 结点v入队列
|
|
|
|
|
3. 当队列非空时,继续执行,否则算法结束
|
|
|
|
|
4. 出队列,取得队头结点u
|
|
|
|
|
5. 查找结点u的第一个邻接结点w
|
|
|
|
|
6. 若结点u的邻接结点w不存在,则转到步骤3;否则循环执行以下三个步骤:
|
|
|
|
|
1. 若结点w尚未被访问,则访问结点w并标记为已访问
|
|
|
|
|
2. 结点w入队列
|
|
|
|
|
3. 查找结点u的继w邻接结点后的下一个邻接结点w,转到步骤6
|
|
|
|
|
|
|
|
|
|
### 广度优先搜索(BFS)
|
|
|
|
|
|
|
|
|
|
广度优先搜索(Breadth-First Search / BFS)是**优先遍历邻居节点**而不是子节点的图遍历算法。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
**BFS一般用来解决最短路径的问题**。和深度优先搜索不同,广度优先的搜索是从起始点出发,一层一层地进行,每层当中的点距离起始点的步数都是相同的,当找到了目的地之后就可以立即结束。广度优先的搜索可以同时从起始点和终点开始进行,称之为双端 BFS。这种算法往往可以大大地提高搜索的效率。
|
|
|
|
|
|
|
|
|
|
#### 代码实现
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
import lombok.AllArgsConstructor;
|
|
|
|
|
import lombok.Data;
|
|
|
|
|
import lombok.NoArgsConstructor;
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Breadth-First Search(BFS)
|
|
|
|
|
* <p>
|
|
|
|
|
* 从根节点出发,在横向遍历二叉树层段节点的基础上纵向遍历二叉树的层次。
|
|
|
|
|
* <p>
|
|
|
|
|
* 数据结构:队列
|
|
|
|
|
* 父节点入队,父节点出队列,先左子节点入队,后右子节点入队。递归遍历全部节点即可
|
|
|
|
|
*
|
|
|
|
|
* @author lry
|
|
|
|
|
*/
|
|
|
|
|
public class BreadthFirstSearch {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 树节点
|
|
|
|
|
*
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
@Data
|
|
|
|
|
@NoArgsConstructor
|
|
|
|
|
@AllArgsConstructor
|
|
|
|
|
public static class TreeNode<V> {
|
|
|
|
|
private V value;
|
|
|
|
|
private List<TreeNode<V>> childList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 模型:
|
|
|
|
|
* .......A
|
|
|
|
|
* ...../ \
|
|
|
|
|
* ....B C
|
|
|
|
|
* .../ \ / \
|
|
|
|
|
* ..D E F G
|
|
|
|
|
* ./ \ / \
|
|
|
|
|
* H I J K
|
|
|
|
|
*/
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
TreeNode<String> treeNodeA = new TreeNode<>("A", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeB = new TreeNode<>("B", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeC = new TreeNode<>("C", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeD = new TreeNode<>("D", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeE = new TreeNode<>("E", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeF = new TreeNode<>("F", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeG = new TreeNode<>("G", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeH = new TreeNode<>("H", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeI = new TreeNode<>("I", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeJ = new TreeNode<>("J", new ArrayList<>());
|
|
|
|
|
TreeNode<String> treeNodeK = new TreeNode<>("K", new ArrayList<>());
|
|
|
|
|
// A->B,C
|
|
|
|
|
treeNodeA.getChildList().add(treeNodeB);
|
|
|
|
|
treeNodeA.getChildList().add(treeNodeC);
|
|
|
|
|
// B->D,E
|
|
|
|
|
treeNodeB.getChildList().add(treeNodeD);
|
|
|
|
|
treeNodeB.getChildList().add(treeNodeE);
|
|
|
|
|
// C->F,G
|
|
|
|
|
treeNodeC.getChildList().add(treeNodeF);
|
|
|
|
|
treeNodeC.getChildList().add(treeNodeG);
|
|
|
|
|
// D->H,I
|
|
|
|
|
treeNodeD.getChildList().add(treeNodeH);
|
|
|
|
|
treeNodeD.getChildList().add(treeNodeI);
|
|
|
|
|
// G->J,K
|
|
|
|
|
treeNodeG.getChildList().add(treeNodeJ);
|
|
|
|
|
treeNodeG.getChildList().add(treeNodeK);
|
|
|
|
|
|
|
|
|
|
System.out.println("递归方式");
|
|
|
|
|
bfsRecursive(Arrays.asList(treeNodeA), 0);
|
|
|
|
|
System.out.println();
|
|
|
|
|
System.out.println("非递归方式");
|
|
|
|
|
bfsNotRecursive(treeNodeA);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 递归遍历
|
|
|
|
|
*
|
|
|
|
|
* @param children
|
|
|
|
|
* @param depth
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
public static <V> void bfsRecursive(List<TreeNode<V>> children, int depth) {
|
|
|
|
|
List<TreeNode<V>> thisChildren, allChildren = new ArrayList<>();
|
|
|
|
|
for (TreeNode<V> child : children) {
|
|
|
|
|
// 打印节点值以及深度
|
|
|
|
|
System.out.print("-->[" + child.getValue().toString() + "," + depth + "]");
|
|
|
|
|
|
|
|
|
|
thisChildren = child.getChildList();
|
|
|
|
|
if (thisChildren != null && thisChildren.size() > 0) {
|
|
|
|
|
allChildren.addAll(thisChildren);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (allChildren.size() > 0) {
|
|
|
|
|
bfsRecursive(allChildren, depth + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 非递归遍历
|
|
|
|
|
*
|
|
|
|
|
* @param tree
|
|
|
|
|
* @param <V>
|
|
|
|
|
*/
|
|
|
|
|
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> node = itemMap.keySet().iterator().next();
|
|
|
|
|
int depth = itemMap.get(node);
|
|
|
|
|
|
|
|
|
|
//打印节点值以及深度
|
|
|
|
|
System.out.print("-->[" + node.getValue().toString() + "," + depth + "]");
|
|
|
|
|
|
|
|
|
|
if (node.getChildList() != null && !node.getChildList().isEmpty()) {
|
|
|
|
|
for (TreeNode<V> child : node.getChildList()) {
|
|
|
|
|
Map<TreeNode<V>, Integer> map = new HashMap<>();
|
|
|
|
|
map.put(child, depth + 1);
|
|
|
|
|
queue.offer(map);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 输出结果
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
递归方式
|
|
|
|
|
-->[A,0]-->[B,1]-->[C,1]-->[D,2]-->[E,2]-->[F,2]-->[G,2]-->[H,3]-->[I,3]-->[J,3]-->[K,3]
|
|
|
|
|
非递归方式
|
|
|
|
|
-->[A,0]-->[B,1]-->[C,1]-->[D,2]-->[E,2]-->[F,2]-->[G,2]-->[H,3]-->[I,3]-->[J,3]-->[K,3]
|
|
|
|
|
```
|
|
|
|
|
|