什么是搜索算法
算法是作用于具有数据结构之上的,大部分搜索算法是基于“图”这种数据结构。这是因为图的表达能力很强,大部分涉及搜索的场景可以抽象为“图”。
所谓"搜索",最直接的理解就是,从图中寻找一个顶点出发到另一个顶点的路径。针对不同的需求和场景,对应有不同的算法。
图有两种主要存储方式:邻接表和邻接矩阵。这次我们用邻接表来存储图,用无向图来举例说明。
public class Graph{
//无向图
private int v; //顶点个数
private LinkedList<Integer> adj[]; //邻接表
public Graph(int v){
this.v = v;
adj = new LinkedList[v];
for(int i = 0;i < v;i++){
adj[i] = new LinkedList<>();
}
}
public void addEdge(int s,int t){
adj[s].add(t);
adj[t].add(s);
}
}
广度优先搜索
广度优先搜索(Breadth First Search,BFS),从直观上说,它其实是一种地毯式层层推进的搜索策略,首先查找离起始顶点s最近的,然后是次近的,依次往外搜索,直到找到终止顶点t。实际上,通过广度优先搜索找到的源点到终点的路径也是顶点s到顶点t的最短路径。
尽管广度优先搜索的原理非常简单,但代码实现相对来讲并不简单。先来看看如何实现广度优先搜索。下面看看代码,其中s表示起始顶点编号,t表示终止顶点编号。我们搜索一条从s到t的路径。
public void bfs(int s,int t){
if(s == t) return;
boolean[] visited = new boolean[v];
visited[s] = true;
Queue<Integer> queue = new LinkedList<>();
queue.add(s);
int[] prev = new int[v];
for(int i = 0;i < v;i++){
prev[i] = -1;
}
while(queue.size() != 0) {
int w = queue.poll();
for (int i = 0; i < adj[w].size; i++) {
int q = adj[w].get(i);
if (!visited[q]) {
prev[q] = w;
if(q == t){
print(prev,s,t);
return;;
}
visited[q] = true;
queue.add(q);
}
}
}
}
private void print(int[] prev,int s,int t){
if(prev[t] != -1 && t != s){
print(prev,s,prev[t]);
}
System.out.println(t + "");
}
广度优先搜索代码实现包含三个重要的辅助变量:visited,queue,prev.
visited数组用来记录已经被访问的顶点,避免顶点被重读访问。如果顶点q已经被访问,那么visited[q]就会被设置为true。
queue是一个队列,队列具有先进先出的特点,很多按层遍历的需求会用到队列。因为广度优先搜索会逐层访问顶点,所以我们只有把第k层顶点都访问完全之后,才能访问第k+1层顶点。当我们访问到底k层顶点时,需要把第k层顶点记录下来,稍后才能通过第k层顶点找到第k+1层顶点。
prev用来记录搜索路径,当从顶点搜索到顶点t后,prev数组中存储从顶点s到顶点t所经历的路径。不过,这个路径时反向存储的。prev[w]存储的是顶点w的前驱节点(也就是路径中的前一个顶点)。我们需要递归来输出正向路径,也就是代码中的print()函数实现的功能。
深度优先搜索(DFS)
![](https://img-blog.csdnimg.cn/20211012213602189.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAQOaIkeWlveiPnOWVig==,size_9,color_FFFFFF,t_70,g_se,x_16)
boolean found = false;
public void dfs(int s,int t){
found = false;
boolean[] visited = new boolean[v];
int[] prev = new int[v];
for(int i = 0;i < v;i++){
prev[i] = -1;
}
recurDfs(s,t,visited,prev);
print(prev,s,t);
}
private void recurDfs(int w,int t,boolean[] visited,int[] prev){
if(found == true) return;
visited[w] = true;
if(w == t){
found = true;
return;
}
for(int i = 0;i < adj[w].size();i++){
int q = adj[w].get(i);
if(!visited[q]){
prev[q] = w;
recurDfs(q,t,visited,prev);
}
}
}