在现实生活中,有许多应用场景会包含很多点以及点点之间的连接,而这些应用场景我们都可以用即将要学习的图 这种数据结构去解决。
图的分类:
按照连接两个顶点的边的不同,可以把图分为以下两种:
1、无向图:边仅仅连接两个顶点,没有其他含义;
2、有向图:边不仅连接两个顶点,并且具有方向;
图的相关术语
1、邻顶点:当两个顶点通过一条边相连时,我们称这两个顶点是相邻的,并且称这条边依附于这两个顶点。
2、度:某个顶点的度就是依附于该顶点的边的个数
3、子图:是一幅图的所有边的子集(包含这些边依附的顶点)组成的图;
4、路径:是由边顺序连接的一系列的顶点组成
5、环: 是一条至少含有一条边且终点和起点相同的路径
6、连通图: 如果图中任意一个顶点都存在一条路径到达另外一个顶点,那么这幅图就称之为连通图
-- 图的存储结构:邻接表
1.使用一个大小为V的数组 Queue[V] adj,把索引看做是顶点;
2.每个索引处adj[v]存储了一个队列,该队列中存储的是所有与该顶点相邻的其他顶点
-- API设计:
-- 代码:
/**
* 图-无向图
*/
public class Graph {
// 顶点的数量
private final int V;
// 边的数量
private int E;
// 邻接表
private Queue<Integer>[] adj;
// 构造方法
public Graph(int v) {
// 初始化顶点的数量
this.V = v;
// 初始化边的数量
this.E = 0;
// 初始化邻接表
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的邻接表队
adj[v].enqueue(w);
// 将v添加到w的邻接表
adj[w].enqueue(v);
// 边的数量+1
E++;
}
// 获取顶点v的邻接表(相邻的所有顶点)
public Queue<Integer> adj(int v) {
return adj[v];
}
}
-- 深度优先搜索
简单来说就是一条路走到黑,没路可走在回来
-- 代码:
/**
* 无向图-深度优先搜索
*/
public class DepthFirstSearch {
// 索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
// 记录有多少个顶点与s顶点相通
private int count;
// 构造深度优先搜索对象,使用深度优先搜索找出图中所有与s顶点相通的顶点
public DepthFirstSearch(Graph G,int s) {
// 创建一个长度与图顶点数一样大小的布尔数组
marked = new boolean[G.V()];
// 搜索图中与顶点相通的所有顶点
dfs(G,s);
}
// 使用深度优先搜索找出图中顶点v的所有相邻顶点
private void dfs(Graph G,int v) {
// 把当前顶点标记为已搜索
marked[v] = true;
// 遍历顶点v的邻接表,得到每一个顶点w
for (Integer w : G.adj(v)) {
// 如果当前顶点没有被搜索过,则递归搜索与顶点w相通的其他顶点
if(!marked[w]) {
dfs(G,w);
}
}
// 相通的顶点数量+1
count++;
}
// 判断顶点w与顶点s是否相通
public boolean marked(int w) {
return marked[w];
}
// 获取与顶点s相通的所有顶点的总数
public int count() {
return count;
-- 广度优先搜索
-- 代码:
/**
* 无向图-广度优先搜索
*/
public class BreadthFirstSearch {
// 顶点是否被搜索标识
private boolean[] marked;
// 记录相通顶点数
private int count;
// 待搜索顶点队列
private Queue<Integer> waitSearch;
// 构造方法
public BreadthFirstSearch(Graph G,int s) {
// 初始标识
marked = new boolean[G.V()];
// 初始化相通数
count = 0;
// 初始化待搜索顶点队列
waitSearch = new Queue<Integer>();
// 使用广度优先搜索,找出所有与s顶点相通的顶点数量
bfs(G,s);
}
// 使用广度优先搜索,找出所有与s顶点相通的顶点数量
private void bfs(Graph G,int s) {
// 标记当前顶点为已搜索
marked[s] = true;
// 当前顶点加入待搜索队列
waitSearch.enqueue(s);
// 从待搜索队列去处顶点,搜索其邻接表
while(!waitSearch.isEmpty()) {
Integer wait = waitSearch.dequeue();
// 遍历邻接表
for (Integer v : G.adj(wait)) {
// 如果当前顶点未被搜索,则递归搜索这个顶点
if(!marked[v]) {
bfs(G,v);
}
}
}
// 相通数量+1
count++;
}
// 判断w顶点是否已s顶点相通
public boolean marked(int w) {
return marked[w];
}
// 返回相通顶点数量
public int count() {
return count;
}
}
-- 路径查找:
1、用深度优先搜索,将路径保存在edgeTo数组中,数组中保存的是每个顶点的上一个顶点。
2、遍历edgeTo数组(路径),将结果加入栈中返回,栈中就是从起点到我们要查找的顶点的路径
/**
* 无向图-路径查找
*/
public class DepthFirstPaths {
// 顶点是否与被搜索标记
private boolean[] marked;
// 起点
private int s;
// 图的所有路径
private int[] edgeTo;
// 构造方法
public DepthFirstPaths(Graph G,int s) {
// 初始化顶点是否被搜索标记
marked = new boolean[G.V()];
// 初始化起点
this.s = s;
// 初始化图的路径
edgeTo = new int[G.V()];
// 使用深度优先搜索,找出图的路径
dfs(G,s);
}
// 使用深度优先搜索,找出图的路径
private void dfs(Graph G,int v) {
// 标记当前路径已被搜索
marked[v] = true;
// 遍历当前顶点邻接表
for (Integer w : G.adj(v)) {
// 如果当前顶点未被搜索,则递归查找当前顶点邻接表
if(!marked[w]) {
edgeTo[w] = v; // 保存路径,原理是保存从上一个顶点到该顶点的路径
dfs(G,s);
}
}
}
// 判断w顶点与s顶点是否有路径
public boolean hasPath(int w) {
return marked[w];
}
// 找出从起点到顶点w的路径
public Stack<Integer> pathTo(int w) {
// 判断一下,w到s之间是否存在路径
if(!hasPath(w)) {
return null;
}
// 创建一个栈保存路径
Stack<Integer> stack = new Stack<>();
// 遍历路径
for(int v = w;v != s;v = edgeTo[v]) {
stack.push(v);
}
// 将起点加入栈中
stack.push(s);
return stack;
}
}
@ 以上内容属于个人笔记