图---java---黑马

news2024/10/28 17:09:49

概念

图是由顶点(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) {
        
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

汽车电子工厂中的防静电监控系统,你了解多少?

在汽车电子制造领域&#xff0c;静电放电(ESD)带来的危害不容忽视。微小的静电放电都可能导致电子元器件损坏&#xff0c;进而引发昂贵的返工、产品召回甚至安全事故。因此&#xff0c;有效的防静电监控系统成为汽车电子工厂保障产品质量和安全生产的关键。 传统的防静电措施主…

全面解释人工智能LLM模型的真实工作原理(三)

前一篇&#xff1a;《全面解释人工智能LLM模型的真实工作原理&#xff08;二&#xff09;》 序言&#xff1a;前面两节中&#xff0c;我们介绍了大语言模型的设计图和实现了一个能够生成自然语言的神经网络。这正是现代先进人工智能语言模型的雏形。不过&#xff0c;目前市面上…

深入分析梧桐数据库SQL查询之挖掘季度销售冠军

在现代商业环境中&#xff0c;对销售数据的深入分析是企业决策过程中不可或缺的一部分。通过分析销售数据&#xff0c;企业可以识别出表现最佳的员工&#xff0c;从而激励团队&#xff0c;优化销售策略&#xff0c;并提高整体业绩。本文将详细介绍如何使用SQL查询来识别每个季度…

Navicat导入Excel数据时数据被截断问题分析与解决方案

目录 前言1. 问题分析1.1 默认字段类型的影响1.2 MySQL诊断机制的限制 2. 解决方案2.1 修改字段长度2.2 修改Excel数据以影响推断2.3 检查导入工具的设置 3. 其他注意事项3.1 注册表的修改3.2 增加自增ID 4. 结语 前言 在数据库的日常操作中&#xff0c;将Excel数据导入MySQL是…

基于Python和OpenCV的疲劳检测系统设计与实现

项目运行 需要先安装Python的相关依赖&#xff1a;pymysql&#xff0c;Django3.2.8&#xff0c;pillow 使用pip install 安装 第一步&#xff1a;创建数据库 第二步&#xff1a;执行SQL语句&#xff0c;.sql文件&#xff0c;运行该文件中的SQL语句 第三步&#xff1a;修改源…

JVM(HotSpot):GC之G1垃圾回收器

文章目录 一、简介二、工作原理三、Young Collection 跨代引用四、大对象问题 一、简介 1、适用场景 同时注重吞吐量&#xff08;Throughput&#xff09;和低延迟&#xff08;Low latency&#xff09;&#xff0c;默认的暂停目标是 200 ms超大堆内存&#xff0c;会将堆划分为…

Unreal Engine5安装Niagara UI Renderer插件

系列文章目录 文章目录 系列文章目录前言一、如何下载安装Niagara UI Renderer插件 前言 在2024.10.24号的今天发现unreal engine官网已经没有虚幻商城了&#xff0c;取而代之的是FAB ‌虚幻商城已经停止运营&#xff0c;Epic Games推出了新的数字资产商店FAB。‌ Epic Games…

vue mysql 直连

最近又搞了个小项目&#xff0c;单机版本的内部考勤管理系统&#xff0c;直接部署在对方的工作电脑上&#xff0c;不需要在云端部署&#xff0c;因为项目本身就小&#xff0c;我就没配置后端同事进行开发&#xff0c;而是通过VUE直连mysql进行业务开发及保存。后期再通过PM2来守…

灵动AI:艺术与科技的融合

灵动AI视频官网地址&#xff1a;https://aigc.genceai.com/ 灵动AI 科技与艺术的完美融合之作。它代表着当下最前沿的影像技术&#xff0c;为我们带来前所未有的视觉盛宴。 AI 视频以强大的人工智能算法为基石&#xff0c;能够自动分析和理解各种场景与主题。无论是壮丽的自然…

快速搭建SpringBoot3+Prometheus+Grafana

快速搭建SpringBoot3PrometheusGrafana 一、搭建SpringBoot项目 1.1 创建SpringBoot项目 1.2 修改pom文件配置 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://…

MySQL之数据库设计

前文我们了解了数据库中库、表等的操作&#xff0c;今天我们来学习如何设计数据库&#xff1f;&#xff1f; 在设计数据库前&#xff0c;我们先来了解什么是范式&#xff1f;&#xff1f; 1. 范式 数据库的范式是一组规则&#xff0c;在设计关系数据库时&#xff0c;遵从不同…

Elasticsearch 解析:倒排索引机制/字段类型/语法/常见问题

Elasticsearch 是一个分布式的开源搜索引擎&#xff0c;广泛用于全文搜索、分析和数据存储。它基于 Apache Lucene 构建&#xff0c;支持 RESTful 风格的 API&#xff0c;使得开发者能够高效地存储和检索数据。本文将详细讲解 Elasticsearch 的基本原理&#xff0c;特别是其倒排…

ctfshow(66->70)--RCE/命令执行漏洞--禁用命令执行函数

Web66 源代码&#xff1a; if(isset($_POST[c])){$c $_POST[c];eval($c); }else{highlight_file(__FILE__); }代码审计&#xff1a; POST传参c&#xff0c;eval进行代码执行。 思路&#xff1a; 由于题目过滤了命令执行函数&#xff0c;所以使用其他方法进行RCE。 先使用c…

一文讲明白大模型分布式逻辑(从GPU通信原语到Megatron、Deepspeed)

1. 背景介绍 如果你拿到了两台8卡A100的机器&#xff08;做梦&#xff09;&#xff0c;你的导师让你学习部署并且训练不同尺寸的大模型&#xff0c;并且写一个说明文档。你意识到&#xff0c;你最需要学习的就是关于分布式训练的知识&#xff0c;因为你可是第一次接触这么多卡…

【ROS概述】概念及环境搭建

学习途径&#xff1a; 教程&#xff1a;Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 课程视频&#xff1a;https://www.bilibili.com/video/BV1Ci4y1L7ZZ 机器人体系 要完全实现一个机器人的系统研发&#xff0c;几乎是“全栈”开发&#xff0c;…

python 爬虫 入门 五、抓取图片、视频

目录 一、图片、音频 二、下载视频&#xff1a; 一、图片、音频 抓取图片的手法在上一篇python 爬虫 入门 四、线程&#xff0c;进程&#xff0c;协程-CSDN博客里面其实有&#xff0c;就是文章中的图片部分&#xff0c;在那一篇文章&#xff0c;初始代码的28&#xff0c;29行…

Python 函数返回值之None类型

什么是None None是类型‘NoneType’字面量&#xff0c;用于表示&#xff1a;空的、无意义的 函数如何返回None 不使用return语句即返回None 主动return None 使用场景 函数返回值if判断变量定义 练习&#xff1a; 练习一&#xff1a;无return语句的函数返回值 # 无ret…

Docker | centos7上对docker进行安装和配置

安装docker docker配置条件安装地址安装步骤2. 卸载旧版本3. yum 安装gcc相关4. 安装需要的软件包5. 设置stable镜像仓库6. 更新yum软件包索引7. 安装docker引擎8. 启动测试9. 测试补充&#xff1a;设置国内docker仓库镜像 10. 卸载 centos7安装docker https://docs.docker.com…

交易逆序对的总数 ---- 分治-归并

题目链接 题目: 分析: 解法一: 暴力解法, 遍历所有的数对, 找到逆序对, 需要两重for循环, 一定会超时解法二: 归并排序的思想如果我们将数组分成两半, 我们在每一半中各找一个数字, 判断是否为逆序对, 再根据归并的思想, 再将一半数组分半, 判断是否为逆序对, 直到数组只有一…

合约门合同全生命周期管理系统:企业智能合同管理的新时代

合约门合同全生命周期管理系统&#xff1a;企业智能合同管理的新时代 1. 引言 随着现代企业的快速发展&#xff0c;合同管理的复杂性日益增加。无论是采购合同、销售合同还是合作协议&#xff0c;合同管理已成为企业运营中至关重要的一环。传统的手工合同管理方式往往效率低下…