深度和广度优先搜索:如何找出社交网络中的三度好友关系
什么是“搜索”算法?
深度优先搜索算法和广度优先搜索算法都是基于“图”这种数据结构的,图这种数据结构表达能力很强,大部分设计搜索的场景都可以抽象成“图”
在图中找出从一个顶点出发,到另一个顶点的路径。方法有很多,比如深度优先、广度优先,还有A* , IDA*等启发式搜索算法,用邻接表来存储图
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);
}
}
广度优先搜索 BFS
先查找离起始顶点最近的,然后是次近的,依次向外搜索
s表示起始顶点,t是终止顶点,搜索一条从s到t的路径,即就是从s到t的最短路径
visited是用来记录已经被访问的顶点,用来避免顶点被重复访问,如果顶点q被访问,相应的visited[q]被设置为true
quene是一个队列,用来存储已经被访问、但是相连的顶点还没有被访问的顶点。因为广度优先搜索是逐层访问的,只有把第k层的顶点都访问完成之后,才能访问第k+1层的顶点,当我们访问到第K层的顶点的时候,需要先把第K层的顶点记录下来,才能通过第K层的顶点来找第K+1层的顶点
prev记录搜索路径,当我们从顶点s开始,广度优先搜索到顶点t,prev数组中存储的是路径,不过这个路径是反向存储的,prev[w]存储的是顶点w是从哪个前驱顶点遍历过来的,比如,我们通过顶点2的邻接表访问到顶点3,那个prev[3] = 2
public void bfs(int s ,int t){
if(s == t ) return;
boolean[] visited = new boolean[v];
visited[s] = true;
Quene<Integer> quene = new LinkedList<>();
quene.add(s);
int[] prev = new int[v];
for(int i = 0 ;i < v ;++i){
prev[i] = -1;
}
while(quene.size() != 0){
int w = quene.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;
quene.add(q);
}
}
}
}
private void print(int[] prev ,int s ,int t ){ //递归打印s->t 的路径
if(prev[t] != -1 && t!= s){
print(prev,s,prev[t]);
}
System.out.print(t+" ");
}
时间复杂度是O(V)
深度优先搜索 DFS
深度优先搜索找出来的路径并不是最短路径,实际上,用的是回溯思想,用递归实现
found这个特殊的变量,作用是当我们已经找到终止顶点t之后,我们就不再递归继续查找了
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);
}
}
}
每条边最多被反问两次,一次是遍历,一次是回复,所以时间复杂度是O(E),E是边的个数
如何找出社交网络上某个用户的三度好友关系?
用广度优先搜索算法,首先,遍历与起始顶点最近的一层顶点,也就是一度好友,然后再遍历与用户距离的边数为2的顶点,二度好友关系,以及边数为3的顶点,就是三度好友关系