一、概述
深度优先搜索英文全称为(Depth-First search),简称DFS。为图的一种遍历方式,所采用的策略可概况为优先选取最后一个被访问到的顶点的邻居。于是,以顶点s为基点的DFS搜索,将首先访问顶点s;再从s所有尚未访问到的邻居中任取其一,并以之为基点,递归地执行DFS搜索。故各顶点被访问到的次序,类似于树的先序遍历,当访问到该顶点没有了邻居顶点,于是就回溯访问,而各顶点被访问完毕的次序,则类似于树的后序遍历。
二、实现
从图中选取一个顶点,作为第一次DFS 的起始顶点,从该顶点中的邻居顶点中任选其一,作为基点,再从该顶点中的邻居顶点中任选其一,依次递归,直到到达最后一个没有邻居顶点的顶点。然后返回上一个顶点,选取它另外一个邻居顶点,然后以此类推。直到所有图中所有顶点被访问完毕。
下图来表示遍历过程
DFS树中v是否为u的祖先,若是(v,u)为前向边(forward edge),否则为后向边(backward edge), 若二者来自相互独立的两个分支,则为跨边(cross edge)
图(u)表示选取D为起始顶点
三、代码
下列代码是Java编写的,用一个整型数值来代表一个顶点
创建图的操作接口
package cn.adt.dfs.graph;
import java.util.Set;
/**
* Created by YangT on 2017-8-11.
* 图的顶点用整形来表示
*/
public interface Graph {
boolean addVertex(Integer v);
Double addEdge(Integer from,Integer to);
boolean addEdge(Integer from,Integer to,Double weigth);
boolean removeVertex(Integer v);
boolean removeEdge(Integer from,Integer to);
Set<Integer> getVertices();
Set<Integer> getNeighbors(Integer v);
int size();
}
一个有向图实例
package cn.adt.dfs.graph;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import cn.adt.dfs.graph.Graph;
/**
* Created by YangT on 2017-8-11.
* 有向图
*/
public class DirectedGraph implements Graph {
private Map<Integer,Set<Integer>> vertexMap = new HashMap<>();
/**
* map的键存放顶点,值存放与该顶点相连的顶点
*/
@Override
public boolean addVertex(Integer v) {
vertexMap.put(v, new HashSet<Integer>());
return true;
}
@Override
public Double addEdge(Integer from, Integer to) {
if(!vertexMap.containsKey(from)) return -1d;
if(!vertexMap.containsKey(to)) return -1d;
vertexMap.get(from).add(to);
return 0d;
}
@Override
public boolean addEdge(Integer from, Integer to, Double weigth) {
//当前不支持带权图
return false;
}
@Override
public boolean removeVertex(Integer v) {
if(!vertexMap.containsKey(v)) return false;
vertexMap.remove(v);
return true;
}
@Override
public boolean removeEdge(Integer from, Integer to) {
if(!vertexMap.containsKey(from)) return false;
if(!vertexMap.containsKey(to)) return false;
vertexMap.get(from).remove(to);
return true;
}
@Override
public Set<Integer> getVertices() {
return vertexMap.keySet();
}
@Override
public Set<Integer> getNeighbors(Integer v) {
return vertexMap.get(v);
}
@Override
public int size() {
return vertexMap.size();
}
}
DFS实现
package cn.graph.dfs;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import cn.adt.dfs.graph.Graph;
/**
* Created by YangT on 2017-8-11.
* 图的顶点用整形来表示
*/
public class DFS {
private Graph graph;
//创建一个时间,来表示整个DFS时间,各顶点的状态
private Integer clock = 0;
//创建各顶点入栈时间map,也就是被发现时间
private Map<Integer,Integer> ftimeMap = new HashMap<>();
//创建各顶点出栈时间map,也就是回溯该顶点时间
private Map<Integer,Integer> dtimeMap = new HashMap<>();
private List<Integer> path = new LinkedList<>();
private Set<Integer> visited = new HashSet<>();
private Stack<Integer> stack = new Stack<>();
public DFS(Graph graph){
this.graph = graph;
}
//搜索
public void search(Integer source){
if(source == null || !graph.getVertices().contains(source))
return;
path.add(source);
visited.add(source);
stack.push(source);
ftimeMap.put(source, ++clock);
for(Integer neighbor : graph.getNeighbors(source)){
//判断该顶点是否已被访问
if(visited.contains(neighbor)) continue;
search(neighbor);
}
Integer v = stack.pop();
dtimeMap.put(v, ++clock);
}
//显示顶点被发现和回溯完成的时间
public void showTime(){
Set<Integer> keys = ftimeMap.keySet();
for (Integer key : keys) {
System.out.print("入栈时间 " + key + ":" + ftimeMap.get(key) + " ");
}
System.out.println(" ");
Set<Integer> dkeys = dtimeMap.keySet();
for (Integer key : dkeys) {
System.out.print("出栈时间 " + key + ":" + dtimeMap.get(key) + " ");
}
}
//检查所有顶点是否都已访问到
public boolean check(){
if(graph.size() != visited.size()){
return false;
}
return true;
}
/**
* 获取访问顺序
*/
public List<Integer> getPath(Integer source){
if(source == null || !graph.getVertices().contains(source))
return null;
search(source);
/**
* DFS从一个顶点开始,不可能访问到所有顶点,因此要另取一个顶点,做第二次DFS
*/
while(!check()){
for (Integer v : graph.getVertices()) {
if(!visited.contains(v)){
search(v);
}
}
}
return path;
}
}
测试代码
package cn.test;
import java.util.List;
import cn.adt.dfs.graph.DirectedGraph;
import cn.adt.dfs.graph.Graph;
import cn.graph.dfs.DFS;
/**
* Created by YangT on 2017-8-11.
* 图的顶点用整形来表示
*/
public class DFSTest {
public static void main(String[] args) {
/**
* 创建一个有向图,顶点用整型数值来表示
*/
Graph g = new DirectedGraph();
for (int i = 1; i < 8; i++) {
g.addVertex(i);
}
g.addEdge(1, 2);
g.addEdge(2, 3);
g.addEdge(1, 3);
g.addEdge(4, 1);
g.addEdge(1, 6);
g.addEdge(4, 5);
g.addEdge(5, 6);
g.addEdge(7, 1);
g.addEdge(7, 3);
g.addEdge(6, 7);
DFS dfs = new DFS(g);
/**
* 从顶点1开始dfs
* 输出各顶点的访问顺序
* 调用showTime方法,输出各顶点的被发现和回溯完成访问该顶点时间
*/
List<Integer> paths = dfs.getPath(1);
for (Integer v : paths) {
System.out.print(v + " ");
}
System.out.println(" ");
dfs.showTime();
}
}
图的构造顶点由整型来代替。测试类创建的图仿照上面图片建立
代码不是很完善,希望能指正。如有错误,希望能指正,谢谢。
参考书籍:数据结构第三版 作者:邓俊辉
参考代码:
https://github.com/sherxon/AlgoDS/blob/master/src/algo/graph/DFS.java