图
概念
图是由顶点(vertex)和边(edge)组成的数据结构,例如
该图有四个顶点:A,B,C,D以及四条有向边,有向图中,边是单向的。
有向 vs 无向
如果是无向图,那么边是双向的,下面是一个无向图例子
度
度是指与该顶点相邻的边的数量
如上图
- A,B,C,E,F这几个顶点度数为2
- D顶点度数为4
有向图中,细分为入度和出度,参考下图
- A(2 out / 0 in)
- B、C、E(1 out / 1 in)
- D(2 out / 2 in)
- F(0 out / 2 in)
权
边可以有权重,代表从源顶点到目标定点的距离、费用、时间或其他度量。
路径被定义为从一个顶点到另一个顶点的一系列连续边,例如上图中【北京】到【上海】有多条路径
- 北京 - 上海
- 北京 - 武汉 - 上海
路径长度
- 不考虑权重,长度就是边的数量
- 考虑权重,一般就是权重累加
环
在有向图中,从一个顶点开始,可以通过若干条有向边返回到该顶点,那么就形成了一个环
图的连通性
如果两个顶点之间存在路径,则这两个顶点是连通的,所有顶点都连通,则改图被称之为连通图,若子图连通,则称为连通分量。
图的表示
如下图,
用邻接矩阵表示为:
A B C D
A 0 1 1 0
B 1 0 0 1
C 1 0 0 1
D 0 1 1 0
使用邻接表表示
A -> B -> C
B -> A -> D
C -> A -> D
D -> B -> C
有向图
用邻接矩阵表示为:
A B C D
A 0 1 1 0
B 0 0 0 1
C 0 0 0 1
D 0 0 0 0
使用邻接表表示
A -> B -> C
B -> D
C -> D
D -> empty
图的java实现
顶点
public class Vertex{
String name;
List<Edge> edges;
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Vertex a = new Vertex("A");
Vertex b = new Vertex("B");
Vertex c = new Vertex("C");
Vertex d = new Vertex("D");
a.edges = List.of(new Edge(b), new Edge(c));
b.edges = List.of(new Edge(d));
c.edges = List.of(new Edge(d));
d.edges = List.of();
}
}
边
public class Edge {
Vertex linked;
int weight;
public Edge(Vertex linked) {
this(linked, 1);
}
public Edge(Vertex linked, int weight) {
this.linked = linked;
this.weight = weight;
}
}
java表示
public class Vertex{
String name;
List<Edge> edges;
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
Vertex v7 = new Vertex("v7");
v1.edges = List.of(new Edge(v6, 14), new Edge(v2, 7), new Edge(v3, 9));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
}
}
深度优先遍历
添加属性是否被访问过 visited
public class Vertex{
String name;
List<Edge> edges;
boolean visited = false; // 是否被访问过
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Vertex a = new Vertex("A");
Vertex b = new Vertex("B");
Vertex c = new Vertex("C");
Vertex d = new Vertex("D");
a.edges = List.of(new Edge(b), new Edge(c));
b.edges = List.of(new Edge(d));
c.edges = List.of(new Edge(d));
d.edges = List.of();
}
// 使用递归
public static void dfs(Vertex vertex) {
vertex.visited = true;
System.out.println(vertex.name);
for(Edges edge : vertex.edges) {
if(!edge.linked.visited) {
dfs(edge.linked);
}
}
}
// 使用栈
public static void dfs3(Vertex v) {
Stack<Vertex> stack = new Stack<>();
stack.push(v);
while (!stack.isEmpty()) {
Vertex pop = stack.pop();
pop.visited = true;
System.out.println(pop.getName());
for(Edge edge : pop.edges) {
if (!edge.linked.visited) {
stack.push(edge.linked);
}
}
}
}
}
广度优先遍历
添加属性是否被访问过 visited
public class Vertex{
String name;
List<Edge> edges;
boolean visited = false; // 是否被访问过
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Vertex a = new Vertex("A");
Vertex b = new Vertex("B");
Vertex c = new Vertex("C");
Vertex d = new Vertex("D");
a.edges = List.of(new Edge(b), new Edge(c));
b.edges = List.of(new Edge(d));
c.edges = List.of(new Edge(d));
d.edges = List.of();
}
// 使用队列
public static void dfs2(Vertex vertex) {
Queue<Vertex> queue = new Queue<>();
queue.offer(vertex);
vertex.visited
while (!queue.isEmpty()) {
Vertex poll = queue.poll();
poll.visited = true;
System.out.println(poll.getName());
for(Edge edge : poll.edges) {
if (!edge.linked.visited) {
edge.linked.visited = true;
queue.offer(edge.linked);
}
}
}
}
}
拓扑排序
顶点类中添加属性 度 inDegree
public class Vertex{
String name;
List<Edge> edges;
int inDegree // 入度
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Vertex a = new Vertex("A");
Vertex b = new Vertex("B");
Vertex c = new Vertex("C");
Vertex d = new Vertex("D");
a.edges = List.of(new Edge(b), new Edge(c));
b.edges = List.of(new Edge(d));
c.edges = List.of(new Edge(d));
d.edges = List.of();
List<Vertex> graph = new ArrayList<>(List.of(a, b, c, d));
// 1.遍历每个顶点得到每个顶点的入度
for(Vertex vertex : graph) {
for(Edge edge : vertex.edges) {
edge.linked.inDegree++;
}
}
// 打印输出
for(Vertex vertex : graph) {
System.out.printlin(vertex.name + ”:“ + vertex.inDegree);
}
// 2.将入度为0的顶点加入到队列中
Queue<Vertex> queue = new Queue<>();
for(Vertex vertex : graph) {
if (vetex.inDegree == 0) {
queue.offer(vertex);
}
}
//3.不断移除队列头部顶点,且打印输出,将相邻顶点的入度减一,若相邻顶点入度减为0加入到队列中
while (!queue.isEmpty()) {
Vertex poll = queue.poll();
System.out.println(poll.name);
for(Edge edge : poll.edges) {
edge.linked.inDegree--;
if (edge.linked.inDegree == 0) {
queue.offer(edge.linked);
}
}
}
}
}
拓扑排序检测环
当拓扑排序中有环时,遍历图中的顶点,并不能将全部顶点遍历到
关于在拓扑排序中检测是否有环,创建 List
集合,在移除队列头部顶点时加入顶点属性 name
,最后将 List
与原集合 graph
比较 size
大小,判断是否有环
public class Vertex{
String name;
List<Edge> edges;
int inDegree; // 入度
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Vertex a = new Vertex("A");
Vertex b = new Vertex("B");
Vertex c = new Vertex("C");
Vertex d = new Vertex("D");
a.edges = List.of(new Edge(b), new Edge(c));
b.edges = List.of(new Edge(d));
c.edges = List.of(new Edge(d));
d.edges = List.of();
List<Vertex> graph = new ArrayList<>(List.of(a, b, c, d));
// 1.遍历每个顶点得到每个顶点的入度
for(Vertex vertex : graph) {
for(Edge edge : vertex.edges) {
edge.linked.inDegree++;
}
}
// 打印输出
for(Vertex vertex : graph) {
System.out.printlin(vertex.name + ”:“ + vertex.inDegree);
}
// 2.将入度为0的顶点加入到队列中
Queue<Vertex> queue = new Queue<>();
for(Vertex vertex : graph) {
if (vetex.inDegree == 0) {
queue.offer(vertex);
}
}
List<String> list = new ArrayList<>();
//3.不断移除队列头部顶点,且打印输出,将相邻顶点的入度减一,若相邻顶点入度减为0加入到队列中
while (!queue.isEmpty()) {
Vertex poll = queue.poll();
// System.out.println(poll.name);
list.add(poll.name);
for(Edge edge : poll.edges) {
edge.linked.inDegree--;
if (edge.linked.inDegree == 0) {
queue.offer(edge.linked);
}
}
}
System.out.println(list.size());
System.out.println(graph.size());
}
}
深度优先搜索
在顶点中添加属性 status
,0 表示未访问,1表示访问中,2访问过
public class Vertex{
String name;
List<Edge> edges;
int inDegree; // 入度
int status; // 0 未访问; 1 访问中; 2已访问
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Vertex a = new Vertex("A");
Vertex b = new Vertex("B");
Vertex c = new Vertex("C");
Vertex d = new Vertex("D");
a.edges = List.of(new Edge(b), new Edge(c));
b.edges = List.of(new Edge(d));
c.edges = List.of(new Edge(d));
d.edges = List.of();
List<Vertex> graph = new ArrayList<>(List.of(a, b, c, d));
Stack<String> stack = new Stack<>();
// 1.遍历每个顶点得到每个顶点的入度
for(Vertex vertex : graph) {
dfs(vertex, stack);
}
System.out.println(stack);
}
public static void dfs(Vertex vertex, Stack<String> stack) {
if (vertex.status == 2) {
return;
}
if (vertex.status == 1) {
throw new RuntimeException("有环");
}
vertex.status = 1;
for(Edge edge : vertex.edges) {
dfs(edge.linked, stack);
}
vertex.status = 2;
stack.push(vertex.name);
}
}
迪克斯特拉-单源最短路径算法
算法描述
-
将所有顶带你标记为未访问。创建一个未访问顶点的集合。
-
为每个顶点分配一个临时距离值
- 对于我们的初始顶点,将其设置为零;
- 对于其它所有顶点,将其设置为无穷大;
-
每次选择最小临时距离的未访问顶点,作为新的当前顶点;
-
对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小
- 例如,1->6的距离为14,而1->3->6的距离为11,这时将距离更新为11
- 否则,将保留上次距离值
-
当前顶点的邻居处理完后,把它从未访问集合中删除;
public class Vertex{
String name;
List<Edge> edges;
int inDegree; // 入度
int status; // 0 未访问; 1 访问中; 2已访问
int dist = INF;
static final INF = Integer.MAX_VALUE;
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Dijkstra {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
v1.edges = List.of(new Edge(v6, 14), new Edge(v2, 7), new Edge(v3, 9));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
List<Vertex> graph = List.of(List.of(v1, v2, v3, v4, v5, v6));
dijkstra(graph, v1);
}
public static void dijkstra(List<Vertex>, Vertex source) {
ArrayList<Vertex> list = new ArrayList<>(graph);
source.dist = 0;
while (!list.isEmpty()) {
// 选取当前顶点
Vertex curr = chooseMinVertex(list);
// 更新当前顶点到邻居的距离
updateNeighboursDist(curr, list);
// 从集合中移除顶点
list.remove(curr);
}
}
public Vertex chooseMinVertex(ArrayList<Vertex> list) {
Vertex curr = list.get(0);
for(Vertex v : list) {
if (v.dist < curr.dist) {
curr = v;
}
}
return curr;
}
public void updateNeighboursDist(Vertex curr, ArrayList<Vertex> list) {
for(Edge edge : curr.edges) {
if (list.contains(edge.linked)) {
int dist = curr.dist + edge.weight;
if (dist< edge.linked.dist) {
edge.linked.dist = dist;
}
}
}
}
}
算法改进
记录路径
public class Vertex{
String name;
List<Edge> edges;
int inDegree; // 入度
int status; // 0 未访问; 1 访问中; 2已访问
int dist = INF;
Vertex pre = null;
static final INF = Integer.MAX_VALUE;
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Dijkstra {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
v1.edges = List.of(new Edge(v6, 14), new Edge(v2, 7), new Edge(v3, 9));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
List<Vertex> graph = List.of(List.of(v1, v2, v3, v4, v5, v6));
dijkstra(graph, v1);
}
public static void dijkstra(List<Vertex> graph, Vertex source) {
ArrayList<Vertex> list = new ArrayList<>(graph);
source.dist = 0;
while (!list.isEmpty()) {
// 选取当前顶点
Vertex curr = chooseMinVertex(list);
// 更新当前顶点到邻居的距离
updateNeighboursDist(curr, list);
// 从集合中移除顶点
list.remove(curr);
// 或者使用visited作为是否被访问的条件, 更新距离时,不需要传入list参数
// curr.visited = true;
}
for(Vertex v : graph) {
System.out.println(v.name + " " + v.dist + " " + (v.pre != null ? v.pre : null));
}
}
public Vertex chooseMinVertex(ArrayList<Vertex> list) {
Vertex curr = list.get(0);
for(Vertex v : list) {
if (v.dist < curr.dist) {
curr = v;
}
}
return curr;
}
public void updateNeighboursDist(Vertex curr, ArrayList<Vertex> list) {
for(Edge edge : curr.edges) {
if (list.contains(edge.linked)) {
int dist = curr.dist + edge.weight;
if (dist< edge.linked.dist) {
edge.linked.dist = dist;
edge.linked.pre = curr;
}
}
}
}
}
优化改进
采用优先级队列存储顶点,使用最小堆改写选择最小距离顶点
public class Dijkstra {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
v1.edges = List.of(new Edge(v6, 14), new Edge(v2, 7), new Edge(v3, 9));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
List<Vertex> graph = List.of(List.of(v1, v2, v3, v4, v5, v6));
dijkstra(graph, v1);
}
public static void dijkstra(List<Vertex> graph, Vertex source) {
PriorityQueue<Vertex> queue = new PriorityQueue<>(Comparator.comparingInt(v -> v.dist));
source.dist = 0;
for(Vertex v : graph) {
queue.offer(v);
}
while (!queue.isEmpty()) {
// 选取当前顶点
Vertex poll = queue.poll();
// 更新当前顶点到相邻顶点的距离
if (!curr.visited) {
updateNeighboursDist(poll, queue);
poll.visited = true;
}
// 移除顶点
queue.poll();
}
for(Vertex v : graph) {
System.out.println(v.name + " " + v.dist + " " + (v.pre != null ? v.pre : null));
}
}
public void updateNeighboursDist(Vertex curr, PriorityQueue<Vertex> queue) {
for(Edge edge : curr.edges) {
if (!edge.linked.visited) {
int dist = curr.dist + edge.weight;
if (dist< edge.linked.dist) {
edge.linked.dist = dist;
edge.linked.pre = curr;
queue.offer(edge.linked);
}
}
}
}
}
不能处理负边情况
Bellman-Ford算法
单源最短路径算法
可以处理负边
public class BellmanFord{
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
v1.edges = List.of(new Edge(v3, 1), new Edge(v2, 2));
v2.edges = List.of(new Edge(v3, 2));
v3.edges = List.of(new Edge(v4, 1));
v4.edges = List.of();
List<Vertex> graph = List.of(List.of(v1, v2, v3, v4));
bellmanFord(graph, v1);
}
public void bellmanFord(List<Vertex> graph, Vertex source) {
source.dist = 0;
for(int i = 0; i < graph.size() - 1; i++) {
for(Vertex v : graph) {
for(Edge e : v.edges) {
if (v.list != Integer.MAX_VALUE && v.dist + e.weight < e.linked.list) {
e.linke.dist = v.dist + e.weight;
e.linked.pre = v;
}
}
}
}
for(Vertex v : graph) {
System.out.println(v + " " + (v != null ? v.name : null));
}
}
}
Floyd-Warshall
多源最短路径算法
可以处理 负边,但是不能处理 负环
使用二维表格记录顶点到顶点的距离
public class Vertex{
String name;
List<Edge> edges;
int inDegree; // 入度
int status; // 0 未访问; 1 访问中; 2已访问
int dist = INF;
Vertex pre = null;
static final INF = Integer.MAX_VALUE;
public Vertex(String name) {
this.name = name;
}
public String getName() {
return name;
}
// 重写hashcode和equals
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) return false;
Vertex vertex = (Vertex) o;
return Object.equals(name, vertex.name);
}
@Override
public int hashCode() {
return name != null ? name.hashcode() : 0;
}
}
public class FloydWarshall{
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
v1.edges = List.of(new Edge(v3, -2));
v2.edges = List.of(new Edge(v3, 3), new Edge(v1, 4));
v3.edges = List.of(new Edge(v4, 2));
v4.edges = List.of(new Edge(v2, -1));
List<Vertex> graph = List.of(List.of(v1, v2, v3, v4));
floydWarshall(graph, v1);
}
public void floydWarshall(List<Vertex> graph, Vertex source) {
int size = graph.size();
int[][] dist = new int[size][size];
// 初始化dist,将外层循环顶点与内层循环顶点相比较,若相等则初始化为0,否则为无穷大
for(int i = 0; i < size; i++) {
Vertex v = graph.get(i);
Map<Vertex, Integer> map = v.edges.stream().collect(Collectors.toMap(e -> e.linked, e ->weight));
for(int j = 0; j < size; j++) {
Vertex u = graph.get(j);
if (v == u) {
dist[i][j] = 0;
} else {
dist[i][j] = map.getOrDefault(u, Integer.MAX_VALUE);
}
}
}
print(dist);
// 看能否借路是否能到达其他顶点
for(int k = 0; k < size; k++) {
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
if (dist[i][k] != Integer.MAX_VALUE && dist[k][j] != Integer.MAX_VALUE && dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
print(dist);
}
}
public void print(int[][] dist){
System.out.println("----------------");
for(int[] row : dist) {
System.out.println(Arrays.stream(row).boxed()
.map(x -> x == Integer.MAX_VALUE ? "♾️" : String.valueOf(x))
.map(s -> String.format("%2s", s))
.collect(Collectors.joining(",", "[", "]")));
}
}
}
优化算法
使用二维数组记录当前顶点从哪个顶点过来的
public class FloydWarshall{
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
v1.edges = List.of(new Edge(v3, -2));
v2.edges = List.of(new Edge(v3, 3), new Edge(v1, 4));
v3.edges = List.of(new Edge(v4, 2));
v4.edges = List.of(new Edge(v2, -1));
List<Vertex> graph = List.of(List.of(v1, v2, v3, v4));
floydWarshall(graph, v1);
}
public void floydWarshall(List<Vertex> graph, Vertex source) {
int size = graph.size();
int[][] dist = new int[size][size];
Vertex[][] pre = new Vertex[size][size];
// 初始化dist,将外层循环顶点与内层循环顶点相比较,若相等则初始化为0,否则为无穷大
for(int i = 0; i < size; i++) {
Vertex v = graph.get(i);
Map<Vertex, Integer> map = v.edges.stream().collect(Collectors.toMap(e -> e.linked, e ->weight));
for(int j = 0; j < size; j++) {
Vertex u = graph.get(j);
if (v == u) {
dist[i][j] = 0;
} else {
dist[i][j] = map.getOrDefault(u, Integer.MAX_VALUE);
pre[i][j] = map.get(u) != null ? v : null;
}
}
}
print(dist);
// 看能否借路是否能到达其他顶点
for(int k = 0; k < size; k++) {
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
if (dist[i][k] != Integer.MAX_VALUE && dist[k][j] != Integer.MAX_VALUE && dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
pre[i][j] = pre[k][j];
}
}
}
print(dist);
}
print(pre);
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
path(pre, graph, i, j);
}
}
}
public void print(int[][] dist){
System.out.println("----------------");
for(int[] row : dist) {
System.out.println(Arrays.stream(row).boxed()
.map(x -> x == Integer.MAX_VALUE ? "♾️" : String.valueOf(x))
.map(s -> String.format("%2s", s))
.collect(Collectors.joining(",", "[", "]")));
}
}
static void path(Vertex[][] pre, List<Vertex> graph, int i, int j) {
LinkedList<String> stack = new LinkedList<>();
System.out.println("[" + graph.get(i).name + "," + graph.get(j).name + "]");
stack.push(graph.get(j).name);
while(i != j) {
Vertex vertex = pre[i][j];
stack.push(vertex.name);
j = graph.indexOf(vertex);
}
System.out.println(stack);
}
}
判断负环
主对角线上出现负值, 则可以证明存在负环
最小生成树
以顶点为核心,每次找到相邻顶点的最小距离,依次处理顶点 ,更新距离。
Prim算法
与迪克斯拉算法相似
public class Dijkstra {
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
v1.edges = List.of(new Edge(v6, 14), new Edge(v2, 7), new Edge(v3, 9));
v2.edges = List.of(new Edge(v4, 15));
v3.edges = List.of(new Edge(v4, 11), new Edge(v6, 2));
v4.edges = List.of(new Edge(v5, 6));
v5.edges = List.of();
v6.edges = List.of(new Edge(v5, 9));
List<Vertex> graph = List.of(List.of(v1, v2, v3, v4, v5, v6));
dijkstra(graph, v1);
}
public static void dijkstra(List<Vertex> graph, Vertex source) {
ArrayList<Vertex> list = new ArrayList<>(graph);
source.dist = 0;
while (!list.isEmpty()) {
// 选取当前顶点
Vertex curr = chooseMinVertex(list);
// 更新当前顶点到邻居的距离
updateNeighboursDist(curr);
// 从集合中移除顶点
list.remove(curr);
// 或者使用visited作为是否被访问的条件, 更新距离时,不需要传入list参数
curr.visited = true;
}
for(Vertex v : graph) {
System.out.println(v.name + " " + v.dist + " " + (v.pre != null ? v.pre : null));
}
}
public Vertex chooseMinVertex(ArrayList<Vertex> list) {
Vertex curr = list.get(0);
for(Vertex v : list) {
if (v.dist < curr.dist) {
curr = v;
}
}
return curr;
}
public void updateNeighboursDist(Vertex curr) {
for(Edge edge : curr.edges) {
if (!edge.linked.visited) {
int dist = edge.weight;
if (dist< edge.linked.dist) { // 这是与迪克斯拉算法的区别
edge.linked.dist = dist;
edge.linked.pre = curr;
}
}
}
}
}
Kruskal克鲁斯卡尔算法
以边为核心 ,逐一处理每条边得到最后的最小生成树
public class Kruskal{
static class Edge implements Comparable<Edge> {
List<Vertex> vertices;
int start;
int end;
int weight;
public Edge(List<Vertex> vertices, int satrt, int end, int weight) {
this.vertices = vertices;
this.start = start;
this.end = end;
this.weight = weight;
}
public Edge(int start, int end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public int compareTo(Object o) {
return Integer.compare(this.weight, o.weight);
}
@OVerride
public void toString() {
return vertices.get(start).name + "<->" + vertices(end).name
}
}
public static void main(String[] args) {
Vertex v1 = new Vertex("v1");
Vertex v2 = new Vertex("v2");
Vertex v3 = new Vertex("v3");
Vertex v4 = new Vertex("v4");
Vertex v5 = new Vertex("v5");
Vertex v6 = new Vertex("v6");
Vertex v7 = new Vertex("v7");
List<Vertex> vertices = List.of(List.of(v1, v2, v3, v4, v5, v6, v7));
PriorityQueue<Edge> queue = new PriorityQueue<>(List.of(
new Edge(vertices, 0, 1, 2),
new Edge(vertices, 0, 2, 4),
new Edge(vertices, 0, 3, 1),
new Edge(vertices, 1, 3, 3),
new Edge(vertices, 1, 4, 10),
new Edge(vertices, 2, 3, 2),
new Edge(vertices, 2, 5, 5),
new Edge(vertices, 3, 4, 7),
new Edge(vertices, 3, 5, 8),
new Edge(vertices, 3, 6, 4),
new Edge(vertices, 4, 6, 6),
new Edge(vertices, 5, 6, 1)
));
kruskal(vertices.size(), queue);
}
public void kruskal(int size, PriorityQueue<Edge> queue) {
List<Edge> list = new ArrayList<>();
DisjoinSet set = new DisjoinSet(size);
while (list.size() < size - 1) {
Edge poll = queue.poll();
int i = set.find(poll.start);
int j = set.find(poll.end);
if (i != j) { // 不相交
list.add(poll);
set.union(poll);
}
}
for(Edge edge : list) {
System.out.println(edge);
}
}
}
并查集
不相交集合
pubic class DisjoinSet{
int[] s;
public DisjoinSet(int size) {
s = new int[size];
for(int i = 0; i < size; i++) {
s[i] = i;
}
}
// find 找的是老大
public int find(int x) {
if(x == s[x]) {
return x;
}
return find(s[x]);
}
// 两个集合相交,即选出新老大,x, y是原老大索引
public void union(int x, int y) {
s[y] = x;
}
public void toString() {
return Arrays.toString();
}
public static void main(String[] args) {
DisjoinSet set = new DisjoinSet(7);
System.out.println(set);
int i = set.find(0);
int j = set.find(3);
System.out.println("老大分别是:" + i + " " + j);
if (i != j) {
set.union(i, j);
System.out.println(set);
}
}
}
路径压缩
pubic class DisjoinSet{
int[] s;
public DisjoinSet(int size) {
s = new int[size];
for(int i = 0; i < size; i++) {
s[i] = i;
}
}
// find 找的是老大---------优化:路径压缩
public int find(int x) {
if(x == s[x]) {
return x;
}
return s[x] = find(s[x]);
}
// 两个集合相交,即选出新老大,x, y是原老大索引
public void union(int x, int y) {
s[y] = x;
}
public void toString() {
return Arrays.toString();
}
public static void main(String[] args) {
}
}
UnionBySize
pubic class DisjoinSet{
int[] s;
int[] size;
public DisjoinSet(int size) {
s = new int[size];
this.size = new int[size];
for(int i = 0; i < size; i++) {
s[i] = i;
this.size[i] = 1;
}
}
// find 找的是老大---------优化:路径压缩
public int find(int x) {
if(x == s[x]) {
return x;
}
return s[x] = find(s[x]);
}
// 两个集合相交,即选出新老大,x, y是原老大索引
public void union(int x, int y) {
/**
if (size[x] > size[y]) {
s[y] = x;
size[x] = size[y] + size[x]; // 更新元素个数
} else {
s[x] = y;
size[y] = size[y] + size[x];
}
**/
//
if (size[y] > size[x]) {
int temp = y;
y = x;
x = temp;
}
s[y] = x;
size[x] = size[y] + size[x]; // 更新元素个数
}
public void toString() {
return "内容:" + Arrays.toString(s) + "\n大小:" + Arrays.toString(size);
}
public static void main(String[] args) {
}
}
public void toString() {
return Arrays.toString();
}
public static void main(String[] args) {
}
}
### UnionBySize
```java
pubic class DisjoinSet{
int[] s;
int[] size;
public DisjoinSet(int size) {
s = new int[size];
this.size = new int[size];
for(int i = 0; i < size; i++) {
s[i] = i;
this.size[i] = 1;
}
}
// find 找的是老大---------优化:路径压缩
public int find(int x) {
if(x == s[x]) {
return x;
}
return s[x] = find(s[x]);
}
// 两个集合相交,即选出新老大,x, y是原老大索引
public void union(int x, int y) {
/**
if (size[x] > size[y]) {
s[y] = x;
size[x] = size[y] + size[x]; // 更新元素个数
} else {
s[x] = y;
size[y] = size[y] + size[x];
}
**/
//
if (size[y] > size[x]) {
int temp = y;
y = x;
x = temp;
}
s[y] = x;
size[x] = size[y] + size[x]; // 更新元素个数
}
public void toString() {
return "内容:" + Arrays.toString(s) + "\n大小:" + Arrays.toString(size);
}
public static void main(String[] args) {
}
}