深度优先搜索和广度优先搜索
在人工智能的运筹学的领域中求解与图相关的应用中,这两个算法被证明是非常有用的,而且,如需高效地研究图的基本性质,例如图的连通性以及图是否存在环,这些算法也是必不可少的。
深度优先搜索
深度优先搜索可以从任意顶点开始访问图的顶点,然后把该顶点标记为已访问,在每次迭代中,该算法紧接着处理与当前顶点邻接的未访问的顶点。这个过程一直持续,直到遇到一个终点——该顶点的所有邻接点都已经被访问过。在该终点上,该算法沿着来路后退一条边,并试着继续从哪里访问未访问的顶点,在后退到起始顶点,并起始顶点也是一个终点时,该算法最终停了下来。这样起始顶点所在的连通分量的所有顶点都被访问过了。如果未访问的顶点仍然存在,该算法必须从其中任一顶点开始,重复上述过程。
用一个栈来跟踪深度优先搜索的操作是比较方便的。在第一次访问一个顶点时,我们把该顶点入栈,当它成为一个终点时,我们把它出栈。
Java实现深度优先搜索:
boolean [] visited;
/**
* @Description: 从n开始进行的深度优先遍历
* @return: 返回结果
* @Author: Mr.Gao
* @Date: 2021/2/15
*/
public void result(int n,DenseGraph denseGraph){
int node = denseGraph.findNode();
visited = new boolean[node];
Stack<Integer> stack = new Stack<>();
stack.push(n);
while(!stack.empty()){
int temp = stack.pop();
visited[temp]=true;
System.out.print(temp+" ");
for (int i = 0; i < node; i++) {
if(visited[i]==false&&denseGraph.getG()[temp][i]==true){
stack.push(i);
}
}
}
}
- 这里的DenseGraph是我们实现的稠密图的类。专门用于实现稠密图。
稠密图代码:
/**
* @program:算法库
* @description:稠密图的创建,使用邻接矩阵
*/
public class DenseGraph {
private int edge; //边数
private int node; //节点数
private boolean direction; //是否为有向图
private boolean[][] g; //图的具体数据
public boolean[][] getG() {
return g;
}
public DenseGraph(int node, boolean direction){
assert node>=0; //一个断言
this.node = node;
this.direction = direction;
this.g = new boolean[node][node];
}
//返回边数
public int findEdge(){
return edge;
}
//返回节点数
public int findNode(){
return node;
}
//为图添加一条边
public void addEdge(int p,int q){
assert p>=0&&p<node;
assert q>=0&&q<node;
if(hasEdge(p,q)){
return;
}
g[p][q]=true;
if(!direction){
g[q][p]=true;
}
edge++;
}
//检验p到q是否有一条边
public boolean hasEdge(int p,int q){
assert p>=0&&p<node;
assert q>=0&&q<node;
return g[p][q];
}
}
- 稠密图我们使用邻接矩阵实现,而稀疏图我们使用邻接链表进行实现
稀疏图的类代码:
import java.util.Vector;
/**
* @program:算法库
* @description:稀疏图的创建,使用邻接链表,这里我们规定从自己到自己有一条默认路径。
*/
public class SparseGraph {
private int node; //节点数
private int edge; //边数
private boolean direction; //是否为有向图
private Vector<Integer>[] g;//图的具体数据
public SparseGraph(int node,boolean direction){
assert node>=0; //设置一个断言
this.node = node;
this.direction = direction;
this.g = (Vector<Integer>[]) new Vector[node];
for (int i = 0; i < node; i++) {
g[i] = new Vector<Integer>();
}
}
//返回边的个数
public int findEdge(){
return edge;
}
//返回节点个数
public int findNode(){
return node;
}
//添加一条从p到q的边
public void addEdge(int p ,int q){
assert p>=0&&p<node;
assert q>=0&&q<node;
assert p!=q;
if(!hasEdge(p,q)){
g[p].add(q);
if(!direction){
g[q].add(p);
}
}
edge++;
}
public boolean hasEdge(int p,int q){
assert p>=0&&p<node;
assert q>=0&&q<node;
if(p==q){
return true;
}
for (int i = 0; i < g[p].size(); i++) {
if(g[p].elementAt(i)==q){
return true;
}
}
return false;
}
}
广度优先搜索:
如果说深度优先搜索表现出来的是一种勇气,那么广度优先搜索表现出来的就是一种严谨。它按照一种同心圆的方式,首先访问所有和初始顶点邻接的顶点,然后是离它两条边的所有未访问顶点,以此类推,直到所有与初始顶点同在一个连通分量中的顶点都访问过了为止,如果仍然存在未被访问的顶点,该算法必须从图的其他连通分量中的任意顶点重新开始。
使用队列来跟踪广度优先搜索的查找是方便的。该队列先从遍历的初始顶点开始,将该顶点标记为已访问,在每次迭代中,该算法找出所有和队头顶点邻接的未访问顶点,把它们标记为已访问,再把它们入队。然后将队头顶点从队列中移走。
Java实现:
boolean[] visited;
/**
* @Description: 使用邻接矩阵实现广度优先搜索
* @return: 返回结果
* @Author: Mr.Gao
* @Date: 2021/2/15
*/
public void result(int n,DenseGraph denseGraph){
int node = denseGraph.findNode();
visited = new boolean[node];
Queue<Integer> queue = new LinkedList<>();
queue.add(n);
while(!queue.isEmpty()){
int temp = queue.poll();
System.out.print(temp+" ");
visited[temp] = true;
for (int i = 0; i < node; i++) {
if(visited[i]==false&&denseGraph.getG()[temp][i]==true){
queue.add(i);
}
}
}
}
- 最后附上深度优先搜索和广度优先搜索的主要性质
- 我太菜了