前言
本博客是博主用于复习数据结构以及算法的博客,如果疏忽出现错误,还望各位指正。
深度优先搜索(DFS的原理)
在实现DFS之前,我们首先来了解一下DFS的原理,我们以表格来实现
D-E之间有条权重为8的边,本来那个图里没有(从一个UP那截的,无伤大雅)
若以B为开始结点,进行DFS,那么顺序就是
B->A 进入A A->B B已经遍历过
A->C 进入C C遍历 没有返回-1到A
A接着从C往后继续往后遍历,没有,返回-1又到了B
B接着A往后遍历,到C,已经遍历,继续到D,
D->B已经遍历,D->E进入E,E里面都已经遍历过返回-1到D
D返回-1到B,B接着往后E,已经遍历,OK结束
基本原理:碰到邻接结点,咱就DFS它,直到“我”的回合(行)结束。
然后同时也说一下与BFS的区别:BFS就是碰到结点先存起来,我先把自己回合结束再说,然后从Queue里面拿,进入新的回合。
(比较抽象,可以去B站搜懒猫老师数据结构,虽然不是Java实现的,但是也挺清晰的)
深度优先搜索(DFS的代码实现)
了解了原理,那咱们来实现
首先,了解两个方法
一个是寻找当前结点的第一个邻接结点
//获取index行 第一个邻接结点
public int getFirstNeighbor(int index){
for(int i = 0;i < vertexList.size();i++){
if(edges[index][i] != Integer.MAX_VALUE){
return i; //找到就返回邻接结点的坐标
}
}
return -1; //没找到的话,返回-1
}
另一个就是寻找当前结点的邻接结点的邻接结点(有点抽象,不过请看代码)
//获取row行 column列之后的第一个邻接结点
public int getNextNeighbor(int row,int column){
for(int i = column + 1;i < vertexList.size();i++){
if(edges[row][i] != Integer.MAX_VALUE){
return i; //找到就返回邻接结点的坐标
}
}
return -1; //没找到的话,返回-1
}
之后,我们就来实现,从某个点的DFS,代码如下:
//DFS实现,先定义一个isVisited布尔数组确认该点是否遍历过,这个isVisited后面完整代码会指出所在位置
public void DFS(int index,boolean[] isVisited){
System.out.print(getVertexByIndex(index)+" "); //打印当前结点
isVisited[index] = true;
//查找index的第一个邻接结点f
int f = getFirstNeighbor(index);
//
while(f != -1){//说明有
if(!isVisited[f]){//f没被访问过
DFS(f,isVisited); //就进入该节点f进行遍历
}
//如果f已经被访问过,从当前 index 行的 f列 处往后找
f = getNextNeighbor(index,f);
}
}
最后,如果要对“所有的”图进行深搜,比如要确定联通分量这种,咱就可以对所有结点进行一个遍历,然后再设立一个什么int count,对其++就行,连通分量就出来了。
//考虑到连通分量,需要对所有结点进行一次遍历,因为有Visited,所以不用考虑冲突情况
public void DFS(){
for(int i=0;i<vertexList.size();i++){
if(!isVisited[i]){
DFS(i,isVisited);
}
}
}
以上,就是DFS的实现方法,寻找邻接点的代码没放到DFS()里面,我觉得是方便理解的,之后是整体代码。
注意:我在基本属性中添加了isVisited这个变量,然后在构造器中进行了实现。
//package GraphTest.Demo;
import java.util.ArrayList;
import java.util.Arrays;
public class Graph{//这是一个图类
/***基础属性***/
int[][] edges; //邻接矩阵存储边
ArrayList<EData> to = new ArrayList<>(); //EData包含start,end,weight三个属性,相当于另一种存储方式,主要是为了实现kruskal算法定义的
ArrayList<String> vertexList = new ArrayList<>(); //存储结点名称,当然你若想用Integer也可以,这个是我自己复习用的
int numOfEdges; //边的个数
boolean[] isVisited;
//构造器
Graph(int n){
this.edges = new int[n][n];
//为了方便,决定讲结点初始化为INF,这也方便后续某些操作
int INF = Integer.MAX_VALUE;
for(int i=0;i<n;i++){
Arrays.fill(edges[i],INF);
}
this.numOfEdges = 0;
this.isVisited = new boolean[n];
}
//插入点
public void insertVertex(String vertex){//看自己个人喜好,我这边是一个一个在主方法里插入点的名称
vertexList.add(vertex);
}
//点的个数
public int getNumOfVertex(){
return vertexList.size();
}
//获取第i个节点的名称
public String getVertexByIndex(int i){
return vertexList.get(i);
}
//获取该节点的下标
public int getIndexOfVertex(String vertex){
return vertexList.indexOf(vertex);
}
//插入边
public void insertEdge(int v1,int v2,int weight){
//注意,这里是无向图
edges[v1][v2] = weight;
edges[v2][v1] = weight;
this.numOfEdges++;
//如果要用Kruskal算法的话这里+
to.add(new EData(v1,v2,weight)); //加入from to这种存储方式
}
//边的个数
public int getNumOfEdge(){
return this.numOfEdges;
}
//得到点到点的权值
public int getWeight(int v1,int v2){//获取v1和v2边的权重
return edges[v1][v2];
}
//打印图
public void showGraph(){
for(int[] line:edges){
System.out.println(Arrays.toString(line));
}
}
//获取index行 第一个邻接结点
public int getFirstNeighbor(int index){
for(int i = 0;i < vertexList.size();i++){
if(edges[index][i] != Integer.MAX_VALUE){
return i; //找到就返回邻接结点的坐标
}
}
return -1; //没找到的话,返回-1
}
//获取row行 column列之后的第一个邻接结点
public int getNextNeighbor(int row,int column){
for(int i = column + 1;i < vertexList.size();i++){
if(edges[row][i] != Integer.MAX_VALUE){
return i; //找到就返回邻接结点的坐标
}
}
return -1; //没找到的话,返回-1
}
//DFS实现,先定义一个isVisited布尔数组确认该点是否遍历过
public void DFS(int index,boolean[] isVisited){
System.out.print(getVertexByIndex(index)+" "); //打印当前结点
isVisited[index] = true;
//查找index的第一个邻接结点f
int f = getFirstNeighbor(index);
//
while(f != -1){//说明有
if(!isVisited[f]){//f没被访问过
DFS(f,isVisited); //就进入该节点f进行遍历
}
//如果f已经被访问过,从当前 i 行的 f列 处往后找
f = getNextNeighbor(index,f);
}
}
//考虑到连通分量,需要对所有结点进行一次遍历,因为有Visited,所以不用考虑冲突情况
public void DFS(){
for(int i=0;i<vertexList.size();i++){
if(!isVisited[i]){
DFS(i,isVisited);
}
}
}
public static void main(String[] args) {
int n = 5;
String[] Vertexs ={"A","B","C","D","E"};
//创建图对象
Graph graph = new Graph(n);
for(String value:Vertexs){
graph.insertVertex(value);
}
graph.insertEdge(0,1,7);
graph.insertEdge(0,2,1);
graph.insertEdge(1,2,6);
graph.insertEdge(1,3,3);
graph.insertEdge(1,4,5);
graph.insertEdge(3,4,8);
graph.showGraph();
// for(EData i : graph.to){
// System.out.println(i.toString());
// }
graph.DFS(1, graph.isVisited);
graph.DFS();//求所有的
}
}
class EData{
//当然,这是为了方便,直接记录结点下标,而不记录像"A"这种
int start;
int end;
int weight;
EData(int start,int end,int weight){
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public String toString() {
return "EData{" +
"start=" + start +
", end=" + end +
", weight=" + weight +
'}';
}
}