10.图(Java)

目录

一、图的入门

1.1 图的实际应用:

1.2 图的定义及分类

1.3 无向图

1.3.1 图的相关术语

1.3.2 图的存储结构

1.3.2.1 邻接矩阵

1.3.2.2 邻接表

1.3.3 图的实现

1.3.3.1 图的API设计

 1.3.3.2 代码实现

1.3.4 图的搜索

1.3.4.1 深度优先搜索

1.3.4.2 广度优先搜索

1.3.5 案例-畅通工程续1

1.3.6 路径查找

1.3.6.1 路径查找API设计

 1.3.6.2 路径查找实现

一、有向图

 1.1 有向图的定义及相关术语

 1.2 有向图API设计

1.3 有向图实现

二、拓扑排序

2.1 检测有向图中的环

2.1.1 检测有向环的API设计

 2.1.2 检测有向环实现

2.2 基于深度优先的顶点排序

2.2.1 顶点排序API设计

2.2.2 顶点排序实现

 2.3 拓扑排序实现

三、加权无向图

 3.1 加权无向图边的表示

 3.2 加权无向图的实现

四、最小生成树

 4.1 最小生成树定义及相关约定

4.2 最小生成树原理

4.2.1 树的性质

4.2.2 切分定理

 4.3 贪心算法

4.4 Prim算法

 4.4.1 Prim算法API设计

4.4.2 Prim算法的实现原理

 4.4.3 代码

4.5 kruskal算法

4.5.1 kruskal算法API设计 

4.5.2 kruskal算法的实现原理

 4.5.3 代码

五、加权有向图

5.1加权有向图边的表示

 5.2 加权有向图的实现

六、最短路径

6.1 最短路径定义及性质

6.2 最短路径树API设计

6.3 松弛技术

 6.4 Dijstra算法实现


一、图的入门

1.1 图的实际应用:

在现实生活中,有许多应用场景会包含很多点以及点点之间的连接,而这些应用场景我们都可以用即将要学习的图 这种数据结构去解决。
地图:
我们生活中经常使用的地图,基本上是由城市以及连接城市的道路组成,如果我们把城市看做是一个一个的点,把道路看做是一条一条的连接,那么地图就是我们将要学习的图这种数据结构

 电路图:

下面是一个我们生活中经常见到的集成电路板,它其实就是由一个一个触点组成,并把触点与触点之间通过线进行连接,这也是我们即将要学习的图这种数据结构的应用场景

1.2 图的定义及分类

定义: 图是由一组顶点和一组能够将两个顶点相连的边组成的

 

 特殊的图:

1. 自环:即一条连接一个顶点和其自身的边;
2. 平行边:连接同一对顶点的两条边;

图的分类:
按照连接两个顶点的边的不同,可以把图分为以下两种:
无向图:边仅仅连接两个顶点,没有其他含义;
有向图:边不仅连接两个顶点,并且具有方向;

1.3 无向图

1.3.1 图的相关术语

相邻顶点:
当两个顶点通过一条边相连时,我们称这两个顶点是相邻的,并且称这条边依附于这两个顶点。
度:
某个顶点的度就是依附于该顶点的边的个数
子图:
是一幅图的所有边的子集 ( 包含这些边依附的顶点 ) 组成的图;
路径:
是由边顺序连接的一系列的顶点组成
环:
是一条至少含有一条边且终点和起点相同的路径

 连通图:

如果图中任意一个顶点都存在一条路径到达另外一个顶点,那么这幅图就称之为连通图
连通子图:
一个非连通图由若干连通的部分组成,每一个连通的部分都可以称为该图的连通子图

1.3.2 图的存储结构

要表示一幅图,只需要表示清楚以下两部分内容即可:
1. 图中所有的顶点;
2. 所有连接顶点的边;
常见的图的存储结构有两种:邻接矩阵和邻接表

1.3.2.1 邻接矩阵

1. 使用一个 V*V 的二维数组 int[V][V] adj, 把索引的值看做是顶点;
2. 如果顶点 v 和顶点 w 相连,我们只需要将 adj[v][w] adj[w][v] 的值设置为 1, 否则设置为 0 即可。

很明显,邻接矩阵这种存储方式的空间复杂度是 V^2 的,如果我们处理的问题规模比较大的话,内存空间极有可能不够用。

1.3.2.2 邻接表

1. 使用一个大小为 V 的数组 Queue[V] adj ,把索引看做是顶点;
2. 每个索引处 adj[v] 存储了一个队列,该队列中存储的是所有与该顶点相邻的其他顶点

很明显,邻接表的空间并不是是线性级别的,所以后面我们一直采用邻接表这种存储形式来表示图。

1.3.3 图的实现

1.3.3.1 图的API设计

 1.3.3.2 代码实现

public class Graph {
//顶点数目
private final 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 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];
}
}

1.3.4 图的搜索

在很多情况下,我们需要遍历图,得到图的一些性质,例如,找出图中与指定的顶点相连的所有顶点,或者判定某个顶点与指定顶点是否相通,是非常常见的需求。
有关图的搜索,最经典的算法有深度优先搜索和广度优先搜索,接下来我们分别讲解这两种搜索算法。

1.3.4.1 深度优先搜索

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。

 很明显,在由于边是没有方向的,所以,如果45顶点相连,那么4会出现在5的相邻链表中,5也会出现在4的相邻链表中,那么为了不对顶点进行重复搜索,应该要有相应的标记来表示当前顶点有没有搜索过,可以使用一个布尔类型的数组 boolean[V] marked,索引代表顶点,值代表当前顶点是否已经搜索,如果已经搜索,标记为true,如果没有搜索,标记为false

API 设计:
代码:
public class DepthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点与s顶点相通
private int count;
//构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相邻顶点
public DepthFirstSearch(Graph G,int s){
//创建一个和图的顶点数一样大小的布尔数组
marked = new boolean[G.V()];
//搜索G图中与顶点s相同的所有顶点
dfs(G,s);
}
//使用深度优先搜索找出G图中v顶点的所有相邻顶点
private void dfs(Graph G, int v){
//把当前顶点标记为已搜索
marked[v]=true;
//遍历v顶点的邻接表,得到每一个顶点w
for (Integer w : G.adj(v)){
//如果当前顶点w没有被搜索过,则递归搜索与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;
}
}

1.3.4.2 广度优先搜索

所谓的深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找兄弟结点,然后找子结点。

public class BreadthFirstSearch {
//索引代表顶点,值表示当前顶点是否已经被搜索
private boolean[] marked;
//记录有多少个顶点与s顶点相通
private int count;
//用来存储待搜索邻接表的点
private Queue<Integer> waitSearch;
//构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻顶点
public BreadthFirstSearch(Graph G, int s) {
//创建一个和图的顶点数一样大小的布尔数组
marked = new boolean[G.V()];
//初始化待搜索顶点的队列
waitSearch = new Queue<Integer>();
//搜索G图中与顶点s相同的所有顶点
dfs(G, s);
}
//使用广度优先搜索找出G图中v顶点的所有相邻顶点
private void dfs(Graph G, int v) {
//把当前顶点v标记为已搜索
marked[v]=true;
//把当前顶点v放入到队列中,等待搜索它的邻接表
waitSearch.enqueue(v);
//使用while循环从队列中拿出待搜索的顶点wait,进行搜索邻接表
while(!waitSearch.isEmpty()){
Integer wait = waitSearch.dequeue();
//遍历wait顶点的邻接表,得到每一个顶点w
for (Integer w : G.adj(wait)) {
//如果当前顶点w没有被搜索过,则递归搜索与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;
}
}

1.3.5 案例-畅通工程续1

某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府 畅通工程 的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。目前的道路状况,9号城市和 10 号城市是否相通? 9 号城市和 8 号城市是否相通?
在我们的测试数据文件夹中有一个 trffiffiffic_project.txt 文件,它就是诚征道路统计表,下面是对数据的解释:

总共有 20 个城市,目前已经修改好了 7 条道路,问 9 号城市和 10 号城市是否相通? 9 号城市和 8 号城市是否相通?
解题思路:
1. 创建一个图 Graph 对象,表示城市;
2. 分别调用
addEdge(0,1),addEdge(6,9),addEdge(3,8),addEdge(5,11),addEdge(2,12),addEdge(6,10),addEdge(4,8) ,表示已经修建好的道路把对应的城市连接起来;
3. 通过 Graph 对象和顶点 9 ,构建 DepthFirstSearch 对象或 BreadthFirstSearch 对象;
4. 调用搜索对象的 marked(10) 方法和 marked(8) 方法,即可得到 9 和城市与 10 号城市以及 9 号城市与 8 号城市是否相通。
代码:
package cn.itcast;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Traffic_Project2 {
public static void main(String[] args) throws Exception {
//创建输入流
BufferedReader reader = new BufferedReader(new
InputStreamReader(Traffic_Project2.class.getClassLoader().getResourceAsStream("traffic_proje
ct.txt")));
//读取城市数目,初始化Graph图
int number = Integer.parseInt(reader.readLine());
Graph G = new Graph(number);
//读取已经修建好的道路数目
int roadNumber = Integer.parseInt(reader.readLine());
//循环读取已经修建好的道路,并调用addEdge方法
for (int i = 0; i < roadNumber; i++) {
String line = reader.readLine();
int p = Integer.parseInt(line.split(" ")[0]);
int q = Integer.parseInt(line.split(" ")[1]);
G.addEdge(p, q);
}
//根据图G和顶点9构建图的搜索对象
//BreadthFirstSearch search = new BreadthFirstSearch(G,9);
DepthFirstSearch search = new DepthFirstSearch(G, 9);
//调用搜索对象的marked(10)方法和marked(8)方法
boolean flag1 = search.marked(10);
boolean fl
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怀化第一深情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值