图---java---黑马

概念

图是由顶点(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. 将所有顶带你标记为未访问。创建一个未访问顶点的集合。

  2. 为每个顶点分配一个临时距离值

    • 对于我们的初始顶点,将其设置为零;
    • 对于其它所有顶点,将其设置为无穷大;
  3. 每次选择最小临时距离的未访问顶点,作为新的当前顶点;

  4. 对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小

    • 例如,1->6的距离为14,而1->3->6的距离为11,这时将距离更新为11
    • 否则,将保留上次距离值
  5. 当前顶点的邻居处理完后,把它从未访问集合中删除;

    在这里插入图片描述

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) {
        
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/902669.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AWS域名注册续费详解

在当今互联网时代&#xff0c;域名是建立在线品牌和业务的重要资产。许多企业和个人选择通过Amazon Web Services&#xff08;AWS&#xff09;进行域名注册&#xff0c;享受其高效的管理工具和强大的基础设施。然而&#xff0c;很多用户在注册域名后&#xff0c;可能会产生一个…

Docker安装ShardingSphere-proxy实现读写分离

1.输入以下命令安装proxy docker run -d \ > -v /test/server/proxy-a/conf:/opt/shardingsphere-proxy/conf \ > -v /test/server/proxy-a/ext-lib:/opt/shardingsphere-proxy/ext-lib \ > -e ES_JAVA_OPTS"-Xmx256m -Xms256M -Xmn128m" \ > -p 3321:33…

NVR录像机汇聚管理EasyNVR多品牌NVR管理工具/设备视频报警功能详解

在科技日新月异的今天&#xff0c;视频监控系统作为现代社会的“第三只眼”&#xff0c;正以前所未有的方式深刻影响着我们的生活与社会结构。从公共场所的安全监控到个人生活的记录分享&#xff0c;视频监控系统以其独特的视角和功能&#xff0c;为社会带来了诸多好处&#xf…

在 Kakarot ZkEVM 上使用 Starknet Scaffold 构建应用

Starknet 和 EVM 我们所知的智能合约世界一直围绕着以太坊虚拟机&#xff08;EVM&#xff09;&#xff0c;其主要语言是 Solidity。 尽管 Starknet 通过 STARKs 为以太坊开辟了新的可能性&#xff0c;但其缺点是它有一个不同的虚拟机 (CairoVM)&#xff0c;这要求开发者学习 …

整合Mybatis-plus及最佳实践

项目引入Mybatis-plus 第一步: 引入starter依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId> </dependency>第二步: 使用MapperScan扫描mapper文件夹 SpringBootApplication Mappe…

安全知识见闻-网络类型、协议、设备、安全

网络类型、协议、设备、安全 本章节包括局域网&#xff08;LAN&#xff09;、城域网&#xff08;MAN&#xff09;和广域网&#xff08;WAN&#xff09;。此外&#xff0c;还涉及了网络协议、网络设备和网络安全的基本概念。 目录 网络类型、协议、设备、安全 一、网络类型 …

2024年项目管理新风向:敏捷开发与瀑布开发,哪个更优?

一、项目管理的多样格局 2024 年&#xff0c;项目管理领域展现出丰富多样的格局。数字化趋势愈发明显&#xff0c;项目管理软件普及度不断提高&#xff0c;据相关资料显示&#xff0c;随着云计算、大数据等技术的成熟&#xff0c;项目管理软件将更加普及&#xff0c;实现项目信…

鼠标增强工具 MousePlus v5.3.9.0 中文绿色版

MousePlus 是一款功能强大的鼠标增强工具&#xff0c;它可以帮助用户提高鼠标操作效率和精准度。该软件可以自定义鼠标的各种功能和行为&#xff0c;让用户根据自己的习惯和需求来调整鼠标的表现。 详细功能 自定义鼠标按钮功能&#xff1a;可以为鼠标的各个按钮设置不同的功能…

Spring Boot 应用开发全攻略:从入门到精通

Spring Boot 应用开发全攻略&#xff1a;从入门到精通 引言 在当今快速发展的软件开发领域&#xff0c;Spring Boot 作为一种快速开发框架&#xff0c;凭借其简洁、易用的特性&#xff0c;赢得了开发者的广泛青睐。无论是微服务架构还是传统的单体应用&#xff0c;Spring Boo…

51单片机之蜂鸣器驱动

1.简介 蜂鸣器是一种一体化结构的电子讯响器&#xff0c;采用直流电压供电&#xff0c;广泛应用于计算机、打印机、 复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。   压电式蜂鸣器主要…

【Unity实战笔记】第二一 · 基于状态模式的角色控制——以UnityChan为例

目录 一 内容摘要二 前言三 状态模式的必要性3.1 非状态模式的角色控制3.2 简易状态模式的角色控制3.3 状态模式3.3.1 IState3.3.2 IdleState3.3.3 RunState3.3.4 JumpState3.3.5 PlayerController_ComplexStateMode3.3.6 注意事项 3.4 SMB 四 基于SMB的角色控制4.1 项目实战案…

【NOIP提高组】 自由落体

【NOIP提高组】 自由落体 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 在高为 H 的天花板上有 n 个小球&#xff0c;体积不计&#xff0c;位置分别为 0&#xff0c;1&#xff0c;2&#xff0c;…&#xff0e;n-1。在地面上有一个小车&…

ECMAScript 标准详解

ECMAScript 是 JavaScript 的基础标准&#xff0c;由 Ecma International 制定。它定义了脚本语言的语法和行为。自 1997 年以来&#xff0c;ECMAScript 经过了多个版本的迭代&#xff0c;每个版本都对 JavaScript 产生了深远的影响。 1. ECMAScript 1 (ES1) 发布时间&#xf…

react18中的受控与非受控组件及ref的使用

受控与非受控组件 受控组件,基于修改 state 的值,修改组件内部的状态&#xff0c;来实现页面的更新&#xff0c;推荐使用 非受控组件&#xff0c;基于 ref 获取 dom 的值&#xff0c;来实现页面的更新,不推荐使用,偶尔特殊的场景会使用 给需要获取的元素设置 ref“xxx”,后期基…

Pytorch学习--如何下载及使用Pytorch中自带数据集,如何把数据集和transforms联合在一起使用

一、标准数据集使用 pytorch官网–标准数据集 这里以CIFAR10数据集为例&#xff1a;CIFAR10 下载数据集 代码&#xff1a; import torchvision train_datatorchvision.datasets.CIFAR10(root"datasets",trainTrue,downloadTrue) test_datatorchvision.datasets.…

【实用知识】Spring Boot 优雅捕捉异常的几种姿势

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

计算机网络:数据链路层 —— 虚拟局域网 VLAN

文章目录 局域网虚拟局域网 VLAN虚拟局域网 VLAN 概述实现机制IEEE 802.1Q帧以太网交换机的接口类型Access 接口Trunk 接口Hybrid 接口不进行人为的VLAN划分划分两个不同VLANTrunk接口去标签后进行转发Trunk接口直接转发 局域网 局域网&#xff08;Local Area Network&#xf…

ICP之点云特征计算

这次我们来聊一下点云的特征计算方法&#xff0c; 主流的有两类 1&#xff1a;基于直方图的特征计算 2&#xff1a;基于签名的特征计算 这次我将介绍基于直方图的方式。 基于直方图的特征方法中&#xff0c;PFH&#xff08;Point Feature Histograms&#xff09;和FPFH&#x…

vue3项目中引入阿里图标库

开篇 本篇的主题是在vue3项目中引入阿里图标库 步骤 注册阿里图标库账号(阿里图标)&#xff0c;并创建项目 将图标加入项目中 将需要的图标先加入购物车&#xff0c;随后加入到项目中 生成项目代码 在项目中生成项目代码&#xff0c;便于后续复制到vue项目中 ## 在vue3项目…

基于Ubuntu24.04,下载并编译Android12系统源码 (一)

1. 前言 1.1 编译源码可以干什么 定制Android系统将最新版本的Android系统刷入到自己的Android设备中将整个系统源码导入到Android Studio中&#xff08;可以不用编译源码来实现&#xff09;。 只要有对应的Android源码版本的android.iml和android.ipr文件&#xff0c;就可以…