**BFS基本思想:**BFS是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,则演算终止。
**DFS基本思想:**DFS是从一个点出发,走到分支的更深处,再回溯搜索下一种可能路径。
**DFS应用的范围:**树形问题(包含可转化为树形的问题),全排列问题,组合问题,部分搜索问题(深搜)。
**解决DFS问题用到的结构容器:**可以根据return的类型来选择不同方式:一般使用栈来解决,也可以用Deque((双端队列),或是StringBuffer(解决字符串拼接问题效率较快)来解决。
在解决DFS以及BFS等搜索遍历问题时我们通常会用到递归法与回溯法。
程序调用自身的编程技巧成为递归。
一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它 通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出 解题过程所需要的多次重复计算,大大地减少了程序的代码量。
——摘自《百度百科》
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
——摘自《百度百科》
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目 标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。
回溯的思路基本如下:当前局面下,我们有若干种选择,所以我们对每一种选择进行尝试。如果发现某种选择违反了某些 限定条件,此时 return;如果尝试某种选择到了最后,发现该选择是正确解,那么就将其加入到解集中。 在这种思想下,我们需要清晰的找出三个要素:选择 (Options),限制 (Restraints),结束条件 (Termination)。
在解决DFS问题时最重要的三点:递归结束条件的选择+状态标记+递归后的恢复(回溯)在某些对搜索时间或是空间要求较高的问题时通常也会需要进行剪枝操作
递归是一种算法结构。递归会出现在子程序中,形式上表现为直接或间接的自己调用自己。典型的例子是阶乘,计算规律 为:n!=n×(n−1)!,如果用JAVA 代码表示,基本如下所示:
public int fac(int n) {
if(n == 1) return n;
else return (n*fac(n - 1));
}
回溯是一种算法思想,它是用递归实现的。回溯的过程类似于穷举法,但回溯有“剪枝”功能,即自我判断过程。例如有求和问题,给定有 7 个元素的组合 [1, 2, 3, 4, 5, 6, 7],求加和为 7 的子集。累加计算中,选择 1+2+3+4 时,判断得到 结果为 10 大于 7,那么后面的 5, 6, 7 就没有必要计算了。这种方法属于搜索过程中的优化,即“剪枝”功能。
用一个比较通俗的说法来解释递归和回溯: 我们在路上走着,前面是一个多岔路口,因为我们并不知道应该走哪条路,所以我们需要尝试。尝试的过程就是一个函数。我们选择了一个方向,后来发现又有一个多岔路口,这时候又需要进行一次选择。所以我们需要在上一次尝试结果的基础 上,再做一次尝试,即在函数内部再调用一次函数,这就是递归的过程。 这样重复了若干次之后,发现这次选择的这条路走不通,这时候我们知道我们上一个路口选错了,所以我们要回到上一个 路口重新选择其他路,这就是回溯的思想
package graph;
import java.util.AbstractMap;
import java.util.LinkedList;
import java.util.Queue;
public class Graph {
private Vertex[] vertices;//节点集
private LinkedList<AbstractMap.SimpleEntry<Integer, String>>[] adj;//邻接表(存储顶点关系)
Graph(String[] arr) {
int size = arr.length;
this.vertices = new Vertex[size];
this.adj = new LinkedList[size];
//初始化操作
for (int i = 0; i < size; i++) {
//存储节点
this.vertices[i] = new Vertex(arr[i], i);
//初始化邻接表
this.adj[i] = new LinkedList<>();
}
}
/**
* dfs深度优先搜索
*
* @param startIndex 起始顶点索引
* @param visited 访问判断数组
*/
private void dfs(int startIndex, boolean[] visited) {
//打印当前节点
System.out.print(this.vertices[startIndex].value);
if (startIndex != this.vertices.length - 1) {
System.out.print("--->");
}
//设置当前节点已访问
visited[startIndex] = true;
//获取当前节点邻接关系
LinkedList<AbstractMap.SimpleEntry<Integer, String>> list = this.adj[startIndex];
//遍历邻接关系表,并设置访问数组
for (int i = 0; i < list.size(); i++) {
AbstractMap.SimpleEntry<Integer, String> entry = list.get(i);
Integer node = entry.getKey();
if (!visited[node]) {
dfs(node, visited);
}
}
}
private void bfs(int startIndex, boolean[] visited) {
Queue<Integer> queue = new LinkedList<>();
//先将开始节点入队
queue.offer(startIndex);
//将第一次访问的节点设置为以访问
visited[startIndex] = true;
while (!queue.isEmpty()) {
//出队
Integer node = queue.poll();
System.out.print(this.vertices[node].value + "--->");
//获取该节点邻接关系
LinkedList<AbstractMap.SimpleEntry<Integer, String>> list = this.adj[node];
//从邻接表中获取下一层节点索引
for (int i = 0; i < list.size(); i++) {
Integer key = list.get(i).getKey();
//将邻接的元素入队(访问过的不用入队)
if (!visited[key]) {
queue.offer(key);
//入对的时候就已经访问了该节点
visited[key] = true;
}
}
}
}
//打印邻接表
private void print() {
System.out.println("----------------邻接表----------------");
for (int i = 0; i < this.adj.length; i++) {
System.out.print(vertices[i].value + ":");
for (int j = 0; j < this.adj[i].size(); j++) {
System.out.print(this.adj[i].get(j).getValue());
if (j != this.adj[i].size() - 1) {
System.out.print("--->");
}
}
System.out.println();
}
}
//节点类
class Vertex {
private String value;//节点内容
private int index;//节点对应的索引
private int weight;//节点权值
Vertex(String value, int index, int weight) {
this.value = value;
this.index = index;
this.weight = weight;
}
Vertex(String value, int index) {
this(value, index, 1);
}
}
public static void main(String[] args) {
String[] arr = {"A", "B", "C", "D", "E", "F"};
Graph graph = new Graph(arr);
//填充各顶点关系
graph.adj[0].add(new AbstractMap.SimpleEntry<>(1, "B"));
graph.adj[0].add(new AbstractMap.SimpleEntry<>(3, "D"));
graph.adj[0].add(new AbstractMap.SimpleEntry<>(5, "F"));
graph.adj[1].add(new AbstractMap.SimpleEntry<>(0, "A"));
graph.adj[1].add(new AbstractMap.SimpleEntry<>(2, "C"));
graph.adj[2].add(new AbstractMap.SimpleEntry<>(1, "B"));
graph.adj[2].add(new AbstractMap.SimpleEntry<>(3, "D"));
graph.adj[2].add(new AbstractMap.SimpleEntry<>(4, "E"));
graph.adj[3].add(new AbstractMap.SimpleEntry<>(1, "A"));
graph.adj[3].add(new AbstractMap.SimpleEntry<>(3, "D"));
graph.adj[3].add(new AbstractMap.SimpleEntry<>(4, "E"));
graph.adj[4].add(new AbstractMap.SimpleEntry<>(2, "C"));
graph.adj[4].add(new AbstractMap.SimpleEntry<>(3, "D"));
graph.adj[5].add(new AbstractMap.SimpleEntry<>(0, "A"));
graph.print();
System.out.println("-------------DFS------------");
graph.dfs(0, new boolean[arr.length]);
System.out.println();
System.out.println("-------------BFS------------");
graph.bfs(0, new boolean[arr.length]);
}
}
DFS代码主要部分
/**
* dfs深度优先搜索
*
* @param startIndex 起始顶点索引
* @param visited 访问判断数组
*/
private void dfs(int startIndex, boolean[] visited) {
//打印当前节点
System.out.print(this.vertices[startIndex].value);
if (startIndex != this.vertices.length - 1) {
System.out.print("--->");
}
//设置当前节点已访问
visited[startIndex] = true;
//获取当前节点邻接关系
LinkedList<AbstractMap.SimpleEntry<Integer, String>> list = this.adj[startIndex];
//遍历邻接关系表,并设置访问数组
for (int i = 0; i < list.size(); i++) {
AbstractMap.SimpleEntry<Integer, String> entry = list.get(i);
Integer node = entry.getKey();
if (!visited[node]) {
dfs(node, visited);
}
}
}
BFS代码主要部分
/**
* bfs深度优先搜索
*
* @param startIndex 起始顶点索引
* @param visited 访问判断数组
*/
private void bfs(int startIndex, boolean[] visited) {
Queue<Integer> queue = new LinkedList<>();
//先将开始节点入队
queue.offer(startIndex);
//将第一次访问的节点设置为以访问
visited[startIndex] = true;
while (!queue.isEmpty()) {
//出队
Integer node = queue.poll();
System.out.print(this.vertices[node].value + "--->");
//获取该节点邻接关系
LinkedList<AbstractMap.SimpleEntry<Integer, String>> list = this.adj[node];
//从邻接表中获取下一层节点索引
for (int i = 0; i < list.size(); i++) {
Integer key = list.get(i).getKey();
//将邻接的元素入队(访问过的不用入队)
if (!visited[key]) {
queue.offer(key);
//入对的时候就已经访问了该节点
visited[key] = true;
}
}
}
}
其中BFS(广度优先算法)并未补充完整,接下来会进一步补充。
在之后会出一些DFS,和BFS类型的题解。