图的深度优先搜索和广度优先搜索
程序员文章站
2022-07-13 08:34:54
...
一、图
在计算机科学中,一个图就是一些顶点的集合,这些顶点通过一系列边结对(连接)。顶点用圆圈表示,边就是这些圆圈之间的连线。顶点之间通过边连接。
基本概念
阶(Order):图G中点集V的大小称作图G的阶。
度(Degree):一个顶点的度是指与该顶点相关联的边的条数,顶点v的度记作d(v)。
入度(In-degree)和出度(Out-degree):对于有向图来说,一个顶点的度可细分为入度和出度。一个顶点的入度是指与其关联的各边之中,以其为终点的边数;出度则是相对的概念,指以该顶点为起点的边数。
子图(Sub-Graph):当图G'=(V',E')其中V‘包含于V,E’包含于E,则G'称作图G=(V,E)的子图。每个图都是本身的子图。
路径(Path):从u到v的一条路径是指一个序列v0,e1,v1,e2,v2,...ek,vk,其中ei的顶点为vi及vi - 1,k称作路径的长度。如果它的起止顶点相同,该路径是“闭”的,反之,则称为“开”的。一条路径称为一简单路径(simple path),如果路径中除起始与终止顶点可以重合外,所有顶点两两不等。
二元组的定义
图G是一个有序二元组(V,E),其中V称为顶集(Vertices Set),E称为边集(Edges set),E与V不相交。它们亦可写成V(G)和E(G)。
E的元素都是二元组,用(x,y)表示,其中x,y∈V。
三元组的定义
图G是指一个三元组(V,E,I),其中V称为顶集,E称为边集,E与V不相交;I称为关联函数,I将E中的每一个元素映射到(v,v)。
如果e被映射到(u,v),那么称边e连接顶点u,v,而u,v则称作e的端点,u,v此时关于e相邻。同时,若两条边i,j有一个公共顶点u,则称i,j关于u相邻。
图的存储-邻接矩阵
图的存储-邻接表
public class Graph {
//顶点数目
private int v;
//边的数目
private int e;
//邻接表
private Queue<Integer>[] adj;
public Graph(int v) {
//初始化定点数量
this.v = v;
//初始化边的数量
this.e = 0;
//初始化邻接表
this.adj = new Queue[v];
for(int i = 0; i < adj.length; i++) {
adj[i] = new LinkedList<>();
}
}
//获取定点数目
public int getV() {
return v;
}
//获取边数目
public int getE() {
return e;
}
//向图中添加一条边v-w
public void addEdge(int v, int w) {
//在无向图中,边是没有方向的,所以该边既可以说是v到w的边,也可以说是从w到v的边,因此,让v出现在w表中并让w出现在v表中
adj[v].offer(w);
adj[w].offer(v);
e++;
}
//获取和定点v相邻的所有定点
public Queue<Integer> adj(int v) {
return adj[v];
}
}
二、图的深度优先搜索
class DepthFirstSearch {
//索引代表顶点,表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点和s顶点相通
private int count;
//构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相通顶点
DepthFirstSearch(Graph graph, int s) {
//初始化mark数组
this.marked = new boolean[graph.getV()];
//初始化定点s相通的顶点的数量
this.count = 0;
dfs(graph, s);
}
//使用深度优先搜索找出G图中v顶点的所有相通顶点
private void dfs(Graph graph, int v) {
//把v顶点标志为已搜索
marked[v] = true;
for(Integer w : graph.adj(v)) {
//判断当前顶点有没有被搜索,如果没有被搜索,则递归调用dfs方法进行深度搜索
if(!marked[w]) {
dfs(graph, w);
}
}
//相通顶点数+1
count++;
}
//判断w顶点是否与s顶点相通
public boolean marked(int w) {
return marked[w];
}
//获取与顶点s相通的所有定点总数
public int count() {
return count;
}
//主类
public static void main(String args[]) {
//准备graph对象
Graph graph = new Graph(13);
graph.addEdge(0, 5);
graph.addEdge(0, 1);
graph.addEdge(0, 2);
graph.addEdge(0, 6);
graph.addEdge(5, 3);
graph.addEdge(5, 4);
graph.addEdge(3, 4);
graph.addEdge(4, 6);
graph.addEdge(7, 8);
graph.addEdge(9, 10);
graph.addEdge(9, 11);
graph.addEdge(9, 12);
graph.addEdge(11, 12);
//准备深度搜索对象
DepthFirstSearch depthFirstSearch = new DepthFirstSearch(graph, 0);
//测试与某个顶点相通顶点数量
System.out.println("与起点0相通的顶点的数量为:" + depthFirstSearch.count());
//测试某个顶点与起点是否相同
System.out.println("顶点0是否与顶点5相通:" + depthFirstSearch.marked(5));
System.out.println("顶点0是否与顶点7相通:" + depthFirstSearch.marked(7));
}
}
三、图的广度优先搜索
class BreadthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录多少个顶点与s顶点相通
private int count;
//用来存储搜索邻接表的点
private Queue<Integer> waitSearch;
//构造广度优先搜索对象,使用广度优先搜索出G图中的s顶点的所有相邻顶点
BreadthFirstSearch(Graph graph, int s) {
this.marked = new boolean[graph.getV()];
this.count = 0;
this.waitSearch = new LinkedList<>();
bfs(graph, s);
}
//使用广度优先搜索出G图中v顶点的所有相邻顶点
private void bfs(Graph graph, int v) {
//把当前顶点v标志为已搜索
marked[v] = true;
//让顶点v进入队列,带搜索
waitSearch.offer(v);
//通过循环,如果队列不为空,则从队列中弹出一个带搜索的顶点进行搜索
while(!waitSearch.isEmpty()) {
//弹出一个待搜索顶点
Integer wait = waitSearch.poll();
//遍历wait顶点的邻接表
for(Integer w : graph.adj(wait)) {
if(!marked[w]) {
bfs(graph, w);
}
}
}
//让相通的节点+1
count++;
}
//判断w顶点是否与s顶点相通
public boolean marked(int w) {
return marked[w];
}
//获取与顶点s相通的所有顶点总数
public int count() {
return count;
}
//主类
public static void main(String args[]) {
//准备graph对象
Graph graph = new Graph(13);
graph.addEdge(0, 5);
graph.addEdge(0, 1);
graph.addEdge(0, 2);
graph.addEdge(0, 6);
graph.addEdge(5, 3);
graph.addEdge(5, 4);
graph.addEdge(3, 4);
graph.addEdge(4, 6);
graph.addEdge(7, 8);
graph.addEdge(9, 10);
graph.addEdge(9, 11);
graph.addEdge(9, 12);
graph.addEdge(11, 12);
//准备深度搜索对象
BreadthFirstSearch breadthFirstSearch = new BreadthFirstSearch(graph, 0);
//测试与某个顶点相通顶点数量
System.out.println("与起点0相通的顶点的数量为:" + breadthFirstSearch.count());
//测试某个顶点与起点是否相同
System.out.println("顶点0是否与顶点5相通:" + breadthFirstSearch.marked(5));
System.out.println("顶点0是否与顶点7相通:" + breadthFirstSearch.marked(7));
}
}