【Java数据结构】04-图(Prim,Kruskal,Dijkstra,topo)

5 图

推荐辅助理解

  1. 【视频讲解】bilibili
    Dijkstra
    在这里插入图片描述
    Prim
    在这里插入图片描述

  2. 【手动可视化】Algorithm Visualizer (https://algorithm-visualizer.org/)

  3. 【手动可视化】Data Structure Visualizations (https://www.cs.usfca.edu/~galles/visualization/Algorithms.html)

5.1 掌握图的定义,包括完全图、连通图、简单路径、有向图、无向图、无环图等,明确理解图和二叉树、树和森林这种结构之间的异同点

图的基本定义

期末考试选择题中考察了连通分量和子图的区别。
图(Graph)是由节点(Vertex)和边(Edge)组成的一种数据结构,表示节点之间的关系。图分为有向图和无向图,边可以有权重。若图的顶点数为n,则它得生成树含有n-1条边。

  1. 节点(Vertex): 表示图中的一个元素或实体。
  2. 边(Edge): 表示节点之间的关系,可以是有向的或无向的。
  3. 度(Degree): 图中与该点相连的边数

无向图:几条边就是几个度(全部顶点的度的和等于边数的两倍)d=2e
有向图:某顶点的度=出度数+入度数(全部顶点的度=所有顶点出度+入度)
子图:就是一个整图的一部分,但是必须子图也是图。且也就是说边和顶点需要一起出现。

图的分类
  1. 有向图(Directed Graph): 边有方向,从一个节点指向另一个节点。
  2. 无向图(Undirected Graph): 边没有方向,两个节点之间的关系是双向的。
  3. 加权图(Weighted Graph): 边带有权重,表示节点之间的距离或成本。
  4. 无权图(Unweighted Graph): 边没有权重,只表示节点之间的连接关系。
  5. 完全图(Complete Graph): 每一对节点之间都有一条边。
图的连通性
  1. 连通图(Connected Graph): 任意两个节点之间都存在路径,即图中没有孤立的节点。
  2. 非连通图(Disconnected Graph): 存在孤立的节点,有些节点之间没有路径。
图的路径和环
  1. 路径(Path): 由边连接的一系列节点,形成的序列称为路径。
  2. 简单路径(Simple Path): 不经过重复节点的路径。
  3. 环(Cycle): 起点和终点相同的路径。
图与树、森林的异同
  1. 相同点: 图、树和森林都是由节点和边构成的数据结构,表示元素之间的关系。
  2. 不同点:
    • 图 vs 树: 树是一种特殊的无环图,所有节点通过唯一的路径相互连接,并且没有孤立的节点。
    • 图 vs 森林: 森林是多棵树的集合,每棵树都是独立的。

5.2 掌握图采用邻接矩阵和邻接表进行存储的差异性

图可以使用邻接矩阵和邻接表两种方式进行存储,它们各有优缺点,适用于不同的场景。
期末考试有考到把给出的无向图的邻接矩阵写出来

邻接矩阵

邻接矩阵是使用二维数组来表示图的连接关系。对于有向图,矩阵中的元素 a[i][j] 表示从节点 i 到节点 j 是否存在边;对于无向图,矩阵是对称的,a[i][j] = a[j][i]

优点:

  1. 查找边的存在性快速: 直接访问矩阵元素即可确定两个节点之间是否有边。
  2. 适用于稠密图: 当图边比较多时,矩阵存储相对紧凑。

缺点:

  1. 浪费空间: 对于稀疏图,大部分元素为 0,会占用大量空间。
  2. 添加或删除节点麻烦: 需要调整整个矩阵的大小。
邻接表

邻接表是通过链表来表示图的连接关系。对于每个节点,用一个链表存储与它相邻的节点。

优点:

  1. 节省空间: 对于稀疏图,只存储存在边的部分,节省空间。
  2. 添加或删除节点方便: 通过调整链表,添加或删除节点相对方便。

缺点:

  1. 查找边的存在性相对慢: 需要遍历链表来确定两个节点之间是否有边。
  2. 适用于稀疏图: 对于密集图,可能会占用更多的空间。
比较
  1. 空间复杂度: 邻接矩阵通常占用更多的空间,而邻接表对于稀疏图更经济。
  2. 时间复杂度: 邻接矩阵在查找边的存在性上更快,而邻接表在添加或删除节点上更快。
  3. 适用场景: 邻接矩阵适用于稠密图,而邻接表适用于稀疏图。
Java 代码示例
1.邻接矩阵
class GraphMatrix {private int[][] matrix;public GraphMatrix(int n) {matrix = new int[n][n];}public void addEdge(int i, int j) {matrix[i][j] = 1;matrix[j][i] = 1; // 对于无向图,设置对称位置的元素}// 其他操作...
}
2.邻接表
import java.util.LinkedList;class GraphList {private int V;private LinkedList<Integer>[] adjList;public GraphList(int V) {this.V = V;adjList = new LinkedList[V];for (int i = 0; i < V; ++i) {adjList[i] = new LinkedList<>();}}public void addEdge(int i, int j) {adjList[i].add(j);adjList[j].add(i); // 对于无向图,添加到两个节点的邻接表中}// 其他操作...
}

5.3 掌握广度优先遍历和深度优先遍历

广度优先遍历(BFS)

广度优先遍历是一种逐层访问图中节点的遍历方法。从起始节点开始,依次访问其所有相邻节点,然后再逐层访问下一层的节点。

算法步骤
  1. 将起始节点放入队列。
  2. 从队列中弹出一个节点,访问该节点并将其所有未访问的相邻节点加入队列。
  3. 重复步骤 2,直到队列为空。
Java 代码示例
import java.util.LinkedList;
import java.util.Queue;class Graph {private int V; // 节点数量private LinkedList<Integer>[] adjList;public Graph(int V) {this.V = V;adjList = new LinkedList[V];for (int i = 0; i < V; ++i) {adjList[i] = new LinkedList<>();}}public void addEdge(int i, int j) {adjList[i].add(j);adjList[j].add(i); // 对于无向图,添加到两个节点的邻接表中}public void bfs(int start) {boolean[] visited = new boolean[V];Queue<Integer> queue = new LinkedList<>();visited[start] = true;queue.offer(start);while (!queue.isEmpty()) {int current = queue.poll();System.out.print(current + " ");for (int neighbor : adjList[current]) {if (!visited[neighbor]) {visited[neighbor] = true;queue.offer(neighbor);}}}}
}
深度优先遍历(DFS)

深度优先遍历是一种递归或栈的方式遍历图的方法。从起始节点开始,访问一个相邻节点,然后递归或压栈访问该节点的未访问相邻节点,直到没有未访问的相邻节点为止,然后回溯到上一层继续。

算法步骤
  1. 从起始节点开始递归或使用栈,访问该节点并标记为已访问。
  2. 对该节点的未访问相邻节点,递归或压栈访问它们。
  3. 重复步骤 2,直到当前路径上没有未访问的相邻节点。
  4. 回溯到上一层,继续步骤 2。
Java 代码示例
import java.util.LinkedList;
import java.util.Stack;class Graph {private int V; // 节点数量private LinkedList<Integer>[] adjList;public Graph(int V) {this.V = V;adjList = new LinkedList[V];for (int i = 0; i < V; ++i) {adjList[i] = new LinkedList<>();}}public void addEdge(int i, int j) {adjList[i].add(j);adjList[j].add(i); // 对于无向图,添加到两个节点的邻接表中}public void dfs(int start) {boolean[] visited = new boolean[V];dfsRecursive(start, visited);}private void dfsRecursive(int current, boolean[] visited) {visited[current] = true;System.out.print(current + " ");for (int neighbor : adjList[current]) {if (!visited[neighbor]) {dfsRecursive(neighbor, visited);}}}
}

5.4 掌握最小生成树(Prim算法、Kruskal算法)、最短路径(Dijkstra算法)、拓扑排序的实现过程

期末考试中,选择题考察了拓扑排序(哪种算法能判断一个图中有没有环),大题中考察了Dijkstra算法(给了有向图写出最短路径,需有过程)

最小生成树

最小生成树(Minimum Spanning Tree,简称 MST)是一个连通图的生成树,其中包含了图中所有的顶点,但是只包含足够的边以使得生成树的总权重最小。两个经典的算法用于找到最小生成树:Prim 算法和 Kruskal 算法。Prim 算法更注重顶点的选择,而 Kruskal 算法更注重边的选择。
动态演示在GreedAlgorithm-prim,kruskal,dijkstra

1. Prim 算法

Prim 算法通过逐步选择连接两棵独立树的最小权重边来构建最小生成树。它始终在当前已选取的顶点集合和未选取的顶点集合之间找到权重最小的边
注:在 Prim 算法中,通常规定图的边的权值不能为0。这是因为 Prim 算法的核心思想是选择具有最小权值的边,然后逐步构建最小生成树。如果存在权值为0的边,它们可能在选择过程中引起混淆。

在这里插入图片描述

Java 代码示例
import java.util.Arrays;class PrimAlgorithm {static final int V = 5;int minKey(int key[], boolean mstSet[]) {int min = Integer.MAX_VALUE, minIndex = -1;for (int v = 0; v < V; v++) {if (!mstSet[v] && key[v] < min) {min = key[v];minIndex = v;}}return minIndex;}void primMST(int graph[][]) {int parent[] = new int[V];int key[] = new int[V];boolean mstSet[] = new boolean[V];Arrays.fill(key, Integer.MAX_VALUE);key[0] = 0;parent[0] = -1;for (int count = 0; count < V - 1; count++) {int u = minKey(key, mstSet);mstSet[u] = true;for (int v = 0; v < V; v++) {if (graph[u][v] != 0 && !mstSet[v] && graph[u][v] < key[v]) {parent[v] = u;key[v] = graph[u][v];}}}printMST(parent, graph);}void printMST(int parent[], int graph[][]) {System.out.println("Edge \tWeight");for (int i = 1; i < V; i++) {System.out.println(parent[i] + " - " + i + "\t" + graph[i][parent[i]]);}}public static void main(String[] args) {PrimAlgorithm t = new PrimAlgorithm();int graph[][] = new int[][]{{0, 2, 0, 6, 0},{2, 0, 3, 8, 5},{0, 3, 0, 0, 7},{6, 8, 0, 0, 9},{0, 5, 7, 9, 0}};t.primMST(graph);}
}
2. Kruskal 算法

Kruskal 算法通过按权重递增的顺序选择边来构建最小生成树。它始终选择不形成环路的边,直到构建完整的最小生成树。

在这里插入图片描述

Java 代码示例:
import java.util.Arrays;class KruskalAlgorithm {class Edge implements Comparable<Edge> {int src, dest, weight;public int compareTo(Edge compareEdge) {return this.weight - compareEdge.weight;}}int V, E;Edge edge[];KruskalAlgorithm(int v, int e) {V = v;E = e;edge = new Edge[E];for (int i = 0; i < e; ++i)edge[i] = new Edge();}int find(int parent[], int i) {if (parent[i] == -1)return i;return find(parent, parent[i]);}void union(int parent[], int x, int y) {int xset = find(parent, x);int yset = find(parent, y);parent[xset] = yset;}void kruskalMST() {Edge result[] = new Edge[V];int e = 0;int i = 0;for (i = 0; i < V; ++i)result[i] = new Edge();Arrays.sort(edge);int parent[] = new int[V];Arrays.fill(parent, -1);i = 0;while (e < V - 1) {Edge nextEdge = edge[i++];int x = find(parent, nextEdge.src);int y = find(parent, nextEdge.dest);if (x != y) {result[e++] = nextEdge;union(parent, x, y);}}System.out.println("Edge \tWeight");for (i = 0; i < e; ++i)System.out.println(result[i].src + " - " + result[i].dest + "\t" + result[i].weight);}public static void main(String[] args) {int V = 4;int E = 5;KruskalAlgorithm graph = new KruskalAlgorithm(V, E);graph.edge[0].src = 0;graph.edge[0].dest = 1;graph.edge[0].weight = 10;graph.edge[1].src = 0;graph.edge[1].dest = 2;graph.edge[1].weight = 6;graph.edge[2].src = 0;graph.edge[2].dest = 3;graph.edge[2].weight = 5;graph.edge[3].src = 1;graph.edge[3].dest = 3;graph.edge[3].weight = 15;graph.edge[4].src = 2;graph.edge[4].dest = 3;graph.edge[4].weight = 4;graph.kruskalMST();}
}
最短路径
Dijkstra 算法

Dijkstra 算法用于找到图中单源最短路径。它从起始节点开始,逐步选择距离最近的节点,并更新到其他节点的最短距离。
在这里插入图片描述

Java 代码示例
import java.util.Arrays;class DijkstraAlgorithm {static final int V = 9;int minDistance(int dist[], boolean sptSet[]) {int min = Integer.MAX_VALUE, min_index = -1;for (int v = 0; v < V; v++) {if (!sptSet[v] && dist[v] <= min) {min = dist[v];min_index = v;}}return min_index;}void printSolution(int dist[]) {System.out.println("Vertex \tDistance from Source");for (int i = 0;i < V; i++)System.out.println(i + " \t" + dist[i]);}void dijkstra(int graph[][], int src) {int dist[] = new int[V];boolean sptSet[] = new boolean[V];Arrays.fill(dist, Integer.MAX_VALUE);dist[src] = 0;for (int count = 0; count < V - 1; count++) {int u = minDistance(dist, sptSet);sptSet[u] = true;for (int v = 0; v < V; v++) {if (!sptSet[v] && graph[u][v] != 0 && dist[u] != Integer.MAX_VALUE &&dist[u] + graph[u][v] < dist[v]) {dist[v] = dist[u] + graph[u][v];}}}printSolution(dist);}public static void main(String[] args) {int graph[][] = new int[][]{{0, 4, 0, 0, 0, 0, 0, 8, 0},{4, 0, 8, 0, 0, 0, 0, 11, 0},{0, 8, 0, 7, 0, 4, 0, 0, 2},{0, 0, 7, 0, 9, 14, 0, 0, 0},{0, 0, 0, 9, 0, 10, 0, 0, 0},{0, 0, 4, 14, 10, 0, 2, 0, 0},{0, 0, 0, 0, 0, 2, 0, 1, 6},{8, 11, 0, 0, 0, 0, 1, 0, 7},{0, 0, 2, 0, 0, 0, 6, 7, 0}};DijkstraAlgorithm t = new DijkstraAlgorithm();t.dijkstra(graph, 0);}
}
拓扑排序

拓扑排序用于有向无环图(DAG),它确定图中节点的线性顺序,使得对于每一条有向边 (u, v),节点 u 在拓扑排序中都出现在节点 v 的前面。

Java 代码示例
import java.util.*;class TopologicalSort {private int V;private LinkedList<Integer> adj[];TopologicalSort(int v) {V = v;adj = new LinkedList[v];for (int i = 0; i < v; ++i)adj[i] = new LinkedList();}void addEdge(int v, int w) {adj[v].add(w);}void topologicalSortUtil(int v, boolean visited[], Stack<Integer> stack) {visited[v] = true;Integer i;Iterator<Integer> it = adj[v].iterator();while (it.hasNext()) {i = it.next();if (!visited[i])topologicalSortUtil(i, visited, stack);}stack.push(v);}void topologicalSort() {Stack<Integer> stack = new Stack<>();boolean visited[] = new boolean[V];for (int i = 0; i < V; i++)visited[i] = false;for (int i = 0; i < V; i++)if (!visited[i])topologicalSortUtil(i, visited, stack);System.out.println("Topological Sort:");while (!stack.empty())System.out.print(stack.pop() + " ");}public static void main(String args[]) {TopologicalSort g = new TopologicalSort(6);g.addEdge(5, 2);g.addEdge(5, 0);g.addEdge(4, 0);g.addEdge(4, 1);g.addEdge(2, 3);g.addEdge(3, 1);g.topologicalSort();}
}

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

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

相关文章

MathType中文网站2024最新版本下载及嵌入word教程

MathType是一款专业的数学公式编辑器,兼容Office word,excel等700多种程序,用于编辑数学试卷、书籍、报刊、论文、幻灯演示等文档轻松输入各种复杂的数学公式和符号。 MathType是一款功能强大的数学公式编辑器&#xff0c;广泛用于编写和编辑数学公式。Word是微软公司推出的文…

【智慧教室】东胜物联为智慧教室解决方案商提供丰富智能网关硬件产品

随着互联网、物联网技术的发展&#xff0c;各行各业迎来数字化转型的机遇。本文以智慧教室为例&#xff0c;阐述物联网技术对传统教室的影响&#xff0c;智慧教室解决方案的优势等。 智慧教室是指借助物联网技术、信息及互联网技术&#xff0c;将现代科技手段融入教育教学过程中…

设计模式—行为型模式之中介者模式

设计模式—行为型模式之中介者模式 中介者模式(Mediator Pattern)&#xff1a;用一个中介对象来封装一系列的对象交互&#xff0c;中介者使各对象不需要显式地相互引用&#xff0c;减少对象间混乱的依赖关系&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之…

【Java SE语法篇】10.String类

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ 文章目录 前言1. String类1.1 字符串的构造1.2 String对象的比…

NLP论文阅读记录 - 2021 | WOS HG-News:基于生成式预训练模型的新闻标题生成

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.相关工作三.本文方法四 实验效果4.1数据集4.2 对比模型4.3实施细节4.4评估指标4.5 实验结果4.6 细粒度分析 五 总结思考 前言 HG-News: News Headline Generation Based on a Generative Pre-…

【数位dp】【C++算法】600. 不含连续1的非负整数

作者推荐 【矩阵快速幂】封装类及测试用例及样例 涉及知识点 数位dp LeetCode600. 不含连续1的非负整数 给定一个正整数 n &#xff0c;请你统计在 [0, n] 范围的非负整数中&#xff0c;有多少个整数的二进制表示中不存在 连续的 1 。 示例 1: 输入: n 5 输出: 5 解释: 下…

刷题 ------ 二分枚举(查找)

文章目录 1.x 的平方根2.第一个错误的版本3.有效的完全平方数4.猜数字大小5.排列硬币6. 寻找比目标字母大的最小字母7. 二分查找8.检查整数以及其两倍数是否存在9. 两个数组间的距离值10.特殊的数组的特征值11.找出数组排序后的目标下标12.和有限的最长子序列13.正整数和负数的…

Day04

今日任务 24.两两交换链表中的节点19.删除链表的倒数第N个节点 160. 链表相交142.环形链表II 24 两两交换链表中的节点 题目链接&#xff1a;https://leetcode.cn/problems/swap-nodes-in-pairs/description/ 方法一&#xff1a;遍历实现 思路&#xff1a; 代码&#xff…

Linux/OpenAdmin

Enumeration nmap 用nmap扫描发现目标对外开放了22和80&#xff0c;端口详细信息如下 从nmap的结果看到&#xff0c;是apache的default page&#xff0c;使用工具跑一下目录&#xff0c;看了官 网文档的结果然后写个小字典节约时间&#xff0c;扫描结果如下 On the page at /…

new Handler(getMainLooper())与new Handler()的区别

Handler 在Android中是一种消息处理机制。 new Handler(); 创建handler对象&#xff0c;常用在已经初始化了 Looper 的线程中调用这个构造函数&#xff08;即非主线程&#xff09;&#xff0c;如果感觉不好理解&#xff0c;可以把Handler handler new Handler() 理解为常用在…

曲面上偏移命令的查找

今天学习老王的SW绘图时&#xff0c;遇到一个命令找不到&#xff0c;查询了一会终于找到了这个命令&#xff0c;防止自己忘记&#xff0c;特此记录一下&#xff0c;这个命令就是“曲面上偏移”&#xff0c;网上好多的教程都是错误的&#xff0c;实际上这个命令没有在曲面里面&a…

MySQL(三)——函数

上期文章 MySQL&#xff08;二&#xff09;——SQL 文章目录 上期文章字符串函数数值函数日期函数流程函数总结 函数&#xff1a;一段可以直接被另一段程序调用的程序或代码 字符串函数 函数功能CONCAT(S1,S2,…Sn)字符串拼接&#xff0c;将S1,S2,…Sn拼接成一个字符串LOWER…

快速前端开发01

前端开发 1 前端开发1.快速开发网站2.浏览器能识别的标签2.1 编码&#xff08;head&#xff09;2.2 title&#xff08;head&#xff09;2.3 标题2.4 div和span2.4.5 超链接2.4.6 图片小结2.4.7 列表2.4.8 表格2.4.9 input系列&#xff08;7个&#xff09;2.4.10 下拉框2.4.11 多…

Flask 项目怎么配置并创建第一个小项目?附上完成第一个小案例截图

目录 1. 为什么要学习 flask&#xff1f; 2. flask 是什么&#xff1f; 3. flask 如何使用&#xff1f; 要安装 Flask&#xff0c;可以按照以下步骤进行&#xff1a; 4. 使用流程 4.1. 新建项目 4.1.1. 打开 pycharm&#xff0c;新建项目 4.1.2. 设置目录&#xff0c;并…

MySql前言

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;MySql&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 数据库有哪些软件&#xff1f;&#xff1f; Mysql MySql数…

14.鸿蒙HarmonyOS App(JAVA)时钟组件计时器倒计时单选按钮复选框开关switch与开关按钮ToggleButton图像组件示范

鸿蒙HarmonyOS App(JAVA) 时钟组件 计时器 倒计时 单选按钮 复选框 开关switch 开关按钮ToggleButton 图像组件 ability_main.xml <?xml version"1.0" encoding"utf-8"?> <DirectionalLayoutxmlns:ohos"http://schemas.huawei.co…

HarmonyOS4.0系列——05、状态管理之@Prop、@Link、@Provide、@Consume,以及@Watch装饰器

状态管理 看下面这张图 Components部分的装饰器为组件级别的状态管理&#xff0c;Application部分为应用的状态管理。开发者可以通过StorageLink/LocalStorageLink 实现应用和组件状态的双向同步&#xff0c;通过StorageProp/LocalStorageProp 实现应用和组件状态的单向同步。…

关于群晖ARPL界面能出现ip但是使用Synology Assistant搜索不到ip问题 及解决方法

文章引用ing304 频道文章&#xff1a;https://qun.qq.com/qqweb/qunpro/share?_wv3&_wwv128&appChannelshare&inviteCode20jx8dPsU2z&contentID1m4NKs&businessType2&from181174&shareSource5&bizka 前言 当进入该界面后 提示IP无法访问&a…

【学习心得】图解Git命令

图解Git命令的图片是在Windows操作系统中的Git Bash里操作截图。关于Git的下载安装和理论学习大家可以先看看我写的另两篇文章。链接我放在下面啦&#xff1a; 【学习心得】Git快速上手_git学习心得-CSDN博客 【学习心得】Git深入学习-CSDN博客 一、初始化仓库 命令&#xff…

eBPF运行时安全

引言 eBPF作为当前linux系统上最为炙手可热的技术&#xff0c;通常被用于网络流量过滤和分析、系统调用跟踪、性能优化、安全监控&#xff0c;当下比较知名的项目有Cilium、Falco等。 Cilium 是一个开源的容器网络和安全性项目&#xff0c;致力于提供高效的容器通信和强大的安…