一、概述
图是由一组顶点和一组能够将两个顶点相连的边组成的
二、邻接矩阵实现(todo)
2.1 API设计
2.2 实现
2.3 测试
三、邻接表实现
存储结构:邻接矩阵、邻接表
3.1 API设计
类名 | Graph |
---|---|
构造方法 | Graph(int V):创建一个包含V个顶点但不包含边的图 |
成员方法 | 1.public int V():获取图中顶点的数量 2.public int E():获取图中边的数量 3.public void addEdge(int v,int w):向图中添加一条边 v-w 4.public Queue adj(int v):获取和顶点v相邻的所有顶点 |
成员变量 | 1.private final int V: 记录顶点数量 2.private int E: 记录边数量 3.private Queue[] adj: 邻接表 |
3.2 实现
public class Graph {
// 顶点数目
private final int V;
// 边的数目
private int E;
// 邻接表
private Queue<Integer>[] adj;
public Graph(int v) {
// 初始化顶点数目
V = v;
// 初始化边的数量
this.E = 0;
// 初始化邻接表
this.adj = new Queue[V];
// 初始化邻接表中的空队列
for (int i = 0; i < adj.length; i++) {
adj[i] = new Queue<Integer>();
}
}
// 获取图中顶点的数量
public int V(){
return V;
}
// 获取图中边的数量
public int E(){
return E;
}
// 向图中添加一条边v-w
public void addEdge(int v, int w){
// 把w添加到v的链表中,这样顶点v就多了一个相邻点w
adj[v].enqueue(w);
// 把v添加到w的链表中,这样顶点w就多了一个相邻点v
adj[w].enqueue(v);
// 边的数目自增1
E++;
}
// 获取和顶点v相邻的所有顶点
public Queue<Integer> adj(int v){
return adj[v];
}
}
3.3 测试
public class GraphTest {
public static void main(String[] args) {
Graph graph = new Graph(10);
graph.addEdge(1,2);
graph.addEdge(1,3);
System.out.println("图的边的条数: " + graph.E());
}
}
四、图的搜索
4.1 深度优先搜索
所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。
4.1.1 API设计
类名 | DepthFirstSearch |
---|---|
构造方法 | DepthFirstSearch(Graph G,int s):构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相通顶点 |
成员方法 | 1.private void dfs(Graph G, int v):使用深度优先搜索找出G图中v顶点的所有相通顶点 2.public boolean marked(int w):判断w顶点与s顶点是否相通 3.public int count():获取与顶点s相通的所有顶点的总数 |
成员变量 | 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索 2.private int count:记录有多少个顶点与s顶点相通 |
4.1.2 实现
/**
* @date 2021/7/8 11:32
*/
public class DepthFirstSearch {
// 索引代表顶点,值代表当前顶点是否已经被搜索
private boolean[] marked;
// 记录有多少个顶点与s顶点相同
private int count;
// 构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相邻顶点
public DepthFirstSearch(Graph G, int s) {
// 初始化marked数组
this.marked = new boolean[G.V()];
// 初始化跟顶点s相通的顶点的数量
this.count = 0;
dfs(G,s);
}
// 使用深度优先搜索找出G图中v顶点的所有相邻顶点
private void dfs(Graph G, int v){
// 把当前顶点标记为已搜索
marked[v] = true;
for (Integer w : G.adj(v)) {
// 判断当前w顶点有没有被搜索过,如果没有被搜索过,则递归调用dfs方法进行深度搜索
if (!marked[w]){
dfs(G,w);
}
}
// 相通顶点数量+1
count++;
}
// 判断w顶点与s顶点是否相通
public boolean marked(int w){
return marked[w];
}
// 获取与顶点s相通的所有顶点的总数
public int count(){
return count;
}
}
4.1.3 测试
// 测试深度优先遍历
public static void test02(){
Graph graph = new Graph(10);
graph.addEdge(1,2);
graph.addEdge(1,3);
DepthFirstSearch depthFirstSearch = new DepthFirstSearch(graph,1);
System.out.println("与顶点1相通的所有顶点的总数:" + depthFirstSearch.count());
System.out.println("顶点2与顶点1是否相通:" + depthFirstSearch.marked(2));
System.out.println("顶点4与顶点1是否相通:" + depthFirstSearch.marked(4));
}
4.5 广度优先搜索
所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找兄弟结点,然后找子结点。
4.5.1 API设计
类名 | BreadthFirstSearch |
---|---|
构造方法 | BreadthFirstSearch(Graph G,int s):构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻顶点 |
成员方法 | 1.private void bfs(Graph G, int v):使用广度优先搜索找出G图中v顶点的所有相邻顶点 2.public boolean marked(int w):判断w顶点与s顶点是否相通 3.public int count():获取与顶点s相通的所有顶点的总数 |
成员变量 | 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索 2.private int count:记录有多少个顶点与s顶点相通 3.private Queue waitSearch: 用来存储待搜索邻接表的点 |
4.5.2 实现
/**
* @date 2021/7/8 14:37
*/
public class BreadthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点与s顶点相通
private int count;
//用来存储待搜索邻接表的点
private Queue<Integer> waitSearch;
// 构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻节点
public BreadthFirstSearch(Graph G, int s) {
this.marked = new boolean[G.V()];
this.count = 0;
this.waitSearch = new Queue<Integer>();
bfs(G,s);
}
// 使用广度优先搜索找出G图中s顶点的所有相邻节点
private void bfs(Graph G, int v){
// 把当前顶点v标识为已搜索
marked[v] = true;
// 让顶点v进入队列,待搜索
waitSearch.enqueue(v);
// 通过循环,如果队列不为空,则从队列中弹出一个带搜索的顶点进行搜索
while (!waitSearch.isEmpty()){
// 弹出一个待搜索的顶点
Integer wait = waitSearch.dequeue();
// 遍历wait顶点的邻接表
for (Integer w : G.adj(wait)) {
if (!marked[w]){
bfs(G, w);
}
}
}
// 让相通的节点加一
count++;
}
// 判断w顶点与s顶点是否相通
public boolean marked(int w){
return marked[w];
}
// 获取与顶点s相通的所有顶点的总数
public int count(){
return count;
}
}
4.5.3 测试
// 测试广度优先遍历
public static void test03(){
Graph graph = new Graph(10);
graph.addEdge(1,2);
graph.addEdge(1,3);
BreadthFirstSearch breadthFirstSearch = new BreadthFirstSearch(graph,1);
System.out.println("与顶点1相通的所有顶点的总数:" + breadthFirstSearch.count());
System.out.println("顶点2与顶点1是否相通:" + breadthFirstSearch.marked(2));
System.out.println("顶点4与顶点1是否相通:" + breadthFirstSearch.marked(4));
}
五、路径查找
实现:基于深度优先搜索、基于广度优先搜索
本文采用基于深度优先遍历来查找路径
5.1 API设计
类名 | DepthFirstPaths |
---|---|
构造方法 | DepthFirstPaths(Graph G,int s):构造深度优先搜索对象,使用深度优先搜索找出G图中起点为s的所有路径 |
成员方法 | 1.private void dfs(Graph G, int v):使用深度优先搜索找出G图中v顶点的所有相邻顶点 2.public boolean hasPathTo(int v):判断v顶点与s顶点是否存在路径 3.public Stack pathTo(int v):找出从起点s到顶点v的路径(就是该路径经过的顶点) |
成员变量 | 1.private boolean[] marked: 索引代表顶点,值表示当前顶点是否已经被搜索 2.private int s:起点 3.private int[] edgeTo:索引代表顶点,值代表从起点s到当前顶点路径上的最后一个顶点 |
5.2 实现
/**
* @date 2021/7/8 15:38
*/
public class DepthFirstPaths {
// 索引代表顶点,值代表当前顶点是否已经被搜索
private boolean[] marked;
// 起点
private int s;
// 路径 索引代表顶点,值代表从起点s到当前顶点路径上的最后一个顶点
private int[] edgeTo;
// 构造深度优先搜索对象,使用深度优先搜索找出G图中起点为s的所有路径
public DepthFirstPaths(Graph G, int s) {
// 初始化marked数组
this.marked = new boolean[G.V()];
// 初始化起点
this.s = s;
// 初始化edgeTo数组
this.edgeTo = new int[G.V()];
dfs(G, s);
}
// 使用深度优先搜索找出G图中v顶点的所有相邻顶点
private void dfs(Graph G, int v){
// 把v标识为已搜索
marked[v] = true;
// 遍历顶点v的邻接表,拿到每一个相邻的顶点,继续递归搜索
for (Integer w : G.adj(v)) {
// 如果顶点w没有被搜索,则继续递归搜索
if (!marked[w]){
edgeTo[w] = v; // 到达顶点w的路径上的最后一个顶点是v
dfs(G, w);
}
}
}
// 判断w顶点与s顶点是否存在路径
public boolean hasPathTo(int v){
return marked[v];
}
// 找出从起点s到顶点v的路径(就是该路径经过的顶点)
public Stack<Integer> pathTo(int v){
if (!hasPathTo(v)){
return null;
}
// 创建栈对象,保存路径中的所有顶点
Stack<Integer> path = new Stack<>();
// 通过循环,从顶点v开始,一直往前找,直到找到起点为止
for (int x = v; x != s ; x = edgeTo[x]) {
path.push(x);
}
// 把起点s放到栈中
path.push(s);
return path;
}
}
5.3 测试
// 测试基于深度优先查找路径
public static void test04(){
Graph graph = new Graph(10);
graph.addEdge(1,2);
graph.addEdge(2,3);
graph.addEdge(3,4);
graph.addEdge(2,4);
DepthFirstPaths depthFirstPaths = new DepthFirstPaths(graph,1);
System.out.println("判断顶点2到顶点1有没有路径:" + depthFirstPaths.hasPathTo(2));
System.out.println("判断顶点4到顶点1有没有路径:" + depthFirstPaths.hasPathTo(6));
Stack<Integer> pathTo4 = depthFirstPaths.pathTo(4);
StringBuilder sb = new StringBuilder();
for (Integer v : pathTo4) {
sb.append(v+"-");
}
sb.deleteCharAt(sb.length()-1);
System.out.println("顶点4到顶点1的路径:" + sb);
}