当前位置: 首页 > news >正文

从0开始的算法(数据结构和算法)基础(七)

       图(graph) 是一种非线性数据结构,由顶点(vertex) 和边 (Edge) 组成。他们之间的相关性没有那么高,链表的线性关系,树状图的派生关系。自由度较高就代表复杂度比较高。
       顶点 (Vertex):图中的基本单位,通常用来表示对象一个节点
       边 (Edge):连接两个顶点的线,可以是有向(Directed)或无向(Undirected)。

图论

图论是数学和计算机科学中的一个重要分支,专门研究图的性质和结构。图是一种非线性数据结构,由顶点(也称为节点)和边(连接顶点的线)组成。图论在许多实际应用中扮演着关键角色,如网络设计、社交网络分析和路线规划等。

  • 社交关系:用户之间的朋友关系。
  • 交通网络:城市之间的道路连接。

2. 图的类型

图可以根据不同的特性进行分类:

  • 无向图:边没有方向,两个顶点之间的关系是双向的。例如:A和B是朋友关系。

  • 有向图:边有方向,表示从一个顶点到另一个顶点的单向关系。例如:用户A关注用户B。

  • 加权图:每条边都有一个权重,通常表示成本、距离或时间。

  • 无环图(DAG):没有回路的图,通常在任务依赖关系中使用。

二、图的表示方法

图的表示方法主要有两种:邻接矩阵和邻接表。

1. 邻接矩阵

邻接矩阵是一个二维数组,其中的元素表示图中顶点之间的连接情况。对于稀疏图会浪费空间。
在图论中,边的添加和删除、顶点的添加和删除是基本的操作。使用邻接矩阵表示图时,这些操作的时间复杂度和具体实现方法如下:

(1)邻接矩阵的结构

假设我们有一个图,使用邻接矩阵 adjMat 来表示。该矩阵的大小为 ( V \times V ),其中 ( V ) 是图中顶点的数量。矩阵的元素 adjMat[i][j] 表示从顶点 ( i ) 到顶点 ( j ) 的边的权重(若无边则为0或无穷大)。

(2) 添加/删除边
  • 添加边:在邻接矩阵中修改指定的边,只需更新 adjMat[i][j]adjMat[j][i]。时间复杂度为 ( O(1) )。

    // 添加边
    void addEdge(int[][] adjMat, int u, int v, int weight) {adjMat[u][v] = weight;adjMat[v][u] = weight; // 无向图
    }
    
  • 删除边:同样,只需将 adjMat[i][j]adjMat[j][i] 设为无穷大(或0)。时间复杂度为 ( O(1) )。

    // 删除边
    void removeEdge(int[][] adjMat, int u, int v) {adjMat[u][v] = Integer.MAX_VALUE; // 或0adjMat[v][u] = Integer.MAX_VALUE; // 无向图
    }
    
(3)添加顶点
  • 添加顶点:在邻接矩阵的尾部添加一行一列,并将所有新元素初始化为无穷大(或0)。时间复杂度为 ( O(V2) )。

    // 添加顶点
    int[][] addVertex(int[][] adjMat, int V) {int[][] newAdjMat = new int[V + 1][V + 1];for (int i = 0; i < V; i++) {for (int j = 0; j < V; j++) {newAdjMat[i][j] = adjMat[i][j];}}// 初始化新行和新列for (int i = 0; i < V + 1; i++) {newAdjMat[V][i] = Integer.MAX_VALUE; // 或0newAdjMat[i][V] = Integer.MAX_VALUE; // 或0}return newAdjMat;
    }
    
(4) 删除顶点
  • 删除顶点:在邻接矩阵中删除一行和一列。若删除的是首行首列,需要将其他元素向左上移动。时间复杂度为 ( O(V^2) )。

    // 删除顶点
    int[][] removeVertex(int[][] adjMat, int V, int vertex) {int[][] newAdjMat = new int[V - 1][V - 1];int newRow = 0, newCol;for (int i = 0; i < V; i++) {if (i == vertex) continue; // 跳过删除的顶点newCol = 0;for (int j = 0; j < V; j++) {if (j == vertex) continue; // 跳过删除的顶点newAdjMat[newRow][newCol] = adjMat[i][j];newCol++;}newRow++;}return newAdjMat;
    }
    
(5)初始化
  • 初始化:传入 ( V ) 个顶点,初始化长度为 ( V ) 的顶点列表 vertices,使用 ( O(V) ) 时间;初始化大小为 ( V \times V ) 的邻接矩阵 adjMat,使用 ( O(V^2) ) 时间。

    // 初始化
    int[][] initializeGraph(int V) {int[][] adjMat = new int[V][V];for (int i = 0; i < V; i++) {for (int j = 0; j < V; j++) {if (i == j) {adjMat[i][j] = 0; // 自环权重为0} else {adjMat[i][j] = Integer.MAX_VALUE; // 或0}}}return adjMat;
    }
    

2. 邻接表

邻接表为每个顶点维护一个列表,列出与其直接相连的顶点。对于大型稀疏图,这种表示方法更加节省空间。

三、图的基本算法

1. 深度优先搜索(DFS)

深度优先搜索是一种用于遍历或搜索图的算法。它从某个顶点开始,尽可能深入到每个分支,然后回溯。

Java实现

import java.util.*;class Graph {private Map<String, List<String>> graph;public Graph() {graph = new HashMap<>();}public void addEdge(String u, String v) {graph.putIfAbsent(u, new ArrayList<>());graph.putIfAbsent(v, new ArrayList<>());graph.get(u).add(v);graph.get(v).add(u);  // 无向图}public void dfs(String start) {Set<String> visited = new HashSet<>();dfsHelper(start, visited);}private void dfsHelper(String vertex, Set<String> visited) {visited.add(vertex);System.out.print(vertex + " ");for (String neighbor : graph.get(vertex)) {if (!visited.contains(neighbor)) {dfsHelper(neighbor, visited);}}}
}// 使用示例
public class Main {public static void main(String[] args) {Graph g = new Graph();g.addEdge("A", "B");g.addEdge("A", "C");g.addEdge("B", "D");g.addEdge("C", "D");System.out.println("DFS Traversal:");g.dfs("A");}
}

2. 广度优先搜索(BFS)

广度优先搜索是一种图的遍历方法,从某个顶点开始,逐层访问其邻接顶点。

Java实现

import java.util.*;class Graph {private Map<String, List<String>> graph;public Graph() {graph = new HashMap<>();}public void addEdge(String u, String v) {graph.putIfAbsent(u, new ArrayList<>());graph.putIfAbsent(v, new ArrayList<>());graph.get(u).add(v);graph.get(v).add(u);  // 无向图}public void bfs(String start) {Set<String> visited = new HashSet<>();Queue<String> queue = new LinkedList<>();visited.add(start);queue.add(start);while (!queue.isEmpty()) {String vertex = queue.poll();System.out.print(vertex + " ");for (String neighbor : graph.get(vertex)) {if (!visited.contains(neighbor)) {visited.add(neighbor);queue.add(neighbor);}}}}
}// 使用示例
public class Main {public static void main(String[] args) {Graph g = new Graph();g.addEdge("A", "B");g.addEdge("A", "C");g.addEdge("B", "D");g.addEdge("C", "D");System.out.println("\nBFS Traversal:");g.bfs("A");}
}

3. Dijkstra算法

Dijkstra算法用于加权图,找到从源顶点到其他顶点的最短路径。

Java实现

import java.util.*;class Graph {private Map<String, Map<String, Integer>> graph;public Graph() {graph = new HashMap<>();}public void addEdge(String u, String v, int weight) {graph.putIfAbsent(u, new HashMap<>());graph.putIfAbsent(v, new HashMap<>());graph.get(u).put(v, weight);graph.get(v).put(u, weight);  // 无向图}public Map<String, Integer> dijkstra(String start) {Map<String, Integer> distances = new HashMap<>();PriorityQueue<Node> minHeap = new PriorityQueue<>(Comparator.comparingInt(node -> node.distance));for (String vertex : graph.keySet()) {distances.put(vertex, Integer.MAX_VALUE);}distances.put(start, 0);minHeap.add(new Node(start, 0));while (!minHeap.isEmpty()) {Node current = minHeap.poll();for (Map.Entry<String, Integer> entry : graph.get(current.vertex).entrySet()) {String neighbor = entry.getKey();int weight = entry.getValue();int distance = current.distance + weight;if (distance < distances.get(neighbor)) {distances.put(neighbor, distance);minHeap.add(new Node(neighbor, distance));}}}return distances;}private static class Node {String vertex;int distance;Node(String vertex, int distance) {this.vertex = vertex;this.distance = distance;}}
}// 使用示例
public class Main {public static void main(String[] args) {Graph g = new Graph();g.addEdge("A", "B", 1);g.addEdge("A", "C", 4);g.addEdge("B", "C", 2);g.addEdge("B", "D", 5);g.addEdge("C", "D", 1);Map<String, Integer> result = g.dijkstra("A");System.out.println("\nDijkstra's shortest path from A:");for (Map.Entry<String, Integer> entry : result.entrySet()) {System.out.println("Distance to " + entry.getKey() + ": " + entry.getValue());}}
}

4. Kruskal算法

Kruskal算法用于求解最小生成树。

Java实现

import java.util.*;class UnionFind {private Map<String, String> parent = new HashMap<>();private Map<String, Integer> rank = new HashMap<>();public void add(String u) {parent.put(u, u);rank.put(u, 0);}public String find(String u) {if (!u.equals(parent.get(u))) {parent.put(u, find(parent.get(u)));  // 路径压缩}return parent.get(u);}public void union(String u, String v) {String rootU = find(u);String rootV = find(v);if (!rootU.equals(rootV)) {if (rank.get(rootU) > rank.get(rootV)) {parent.put(rootV, rootU);} else if (rank.get(rootU) < rank.get(rootV)) {parent.put(rootU, rootV);} else {parent.put(rootV, rootU);rank.put(rootU, rank.get(rootU) + 1);}}}
}class Graph {private List<Edge> edges = new ArrayList<>();public void addEdge(String u, String v, int weight) {edges.add(new Edge(u, v, weight));}public List<Edge> kruskal() {edges.sort(Comparator.comparingInt(edge -> edge.weight));UnionFind uf = new UnionFind();List<Edge> mst = new ArrayList<>();for (Edge edge : edges) {uf.add(edge.u);uf.add(edge.v);}for (Edge edge : edges) {if (uf.find(edge.u) != uf.find(edge.v)) {uf.union(edge.u, edge.v);mst.add(edge);}}return mst;}private static class Edge {String u;String v;int weight;Edge(String u, String v, int weight) {this.u = u;this.v = v;this.weight = weight;}}
}// 使用示例
public class Main {public static void main(String[] args) {Graph g = new Graph();g.addEdge("A", "B", 1);g.addEdge("A", "C", 3);g.addEdge("B", "C", 1);g.addEdge("B", "D", 4);g.addEdge("C", "D", 1);List<Graph.Edge> mst = g.kruskal();System.out.println("\nKruskal's Minimum Spanning Tree:");for (Graph.Edge edge : mst) {System.out.println(edge.u + " -- " + edge.v + " == " + edge.weight);}}
}

四、图的应用

图论在现实生活中的应用广泛,以下是一些例子:

  1. 社交网络:分析用户之间的关系,推荐朋友。

    • 例如,Facebook使用图算法来分析用户之间的连接,提供好友推荐。
  2. 地图导航:计算城市之间的最短路径。

    • Google Maps使用图算法来查找最优路线,考虑交通状况和距离。
  3. 网络流:优化网络中的数据流量。

    • 如最大流算法用于找出网络中可传输的最大数据流量。
  4. 任务调度:管理项目任务之间的依赖关系。

    • 在项目管理中,DAG用于表示任务先后顺序,帮助制定合理的调度计划。

持续更新中~~

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Unity Addressables bundle依赖查看和资源重复查看工具
  • linux 多进程搭建webserver
  • MinGW-w64编译安装Acise
  • 维吉尼亚密码加解密实现(python)
  • Android 12系统源码_多屏幕(一)多屏幕设备显示Activity
  • 超声波眼镜清洗机哪个性价比高?2024推荐四款清洁力高的超声波清洗机
  • 第十一章:图论part06 108.冗余连接 109.冗余连接II (补)
  • 3、pnpm yarn npm
  • MySQL笔记(十):视图
  • 【力扣】70.爬楼梯
  • 嵌入式初学-C语言-十七
  • 算法板子:分解质因数
  • 【等保测评】网络安全服务认证技术规范(等级保护测评)
  • openEuler 自定义ISO制作(logo,名称,ISO)
  • LeetCode刷题笔记第17题:电话号码的字母组合
  • 【Linux系统编程】快速查找errno错误码信息
  • centos安装java运行环境jdk+tomcat
  • CODING 缺陷管理功能正式开始公测
  • codis proxy处理流程
  • HTTP 简介
  • Java超时控制的实现
  • MD5加密原理解析及OC版原理实现
  • Python利用正则抓取网页内容保存到本地
  • rabbitmq延迟消息示例
  • Vue UI框架库开发介绍
  • 仿天猫超市收藏抛物线动画工具库
  • 基于组件的设计工作流与界面抽象
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 微信小程序:实现悬浮返回和分享按钮
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • 阿里云API、SDK和CLI应用实践方案
  • 小白应该如何快速入门阿里云服务器,新手使用ECS的方法 ...
  • ​iOS安全加固方法及实现
  • ​VRRP 虚拟路由冗余协议(华为)
  • ​如何防止网络攻击?
  • (3)nginx 配置(nginx.conf)
  • (55)MOS管专题--->(10)MOS管的封装
  • (C++20) consteval立即函数
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (Matlab)使用竞争神经网络实现数据聚类
  • (创新)基于VMD-CNN-BiLSTM的电力负荷预测—代码+数据
  • (附源码)小程序 交通违法举报系统 毕业设计 242045
  • (计算机网络)物理层
  • (十七)Flink 容错机制
  • (转)人的集合论——移山之道
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .NET 中 GetProcess 相关方法的性能
  • .NET/C# 避免调试器不小心提前计算本应延迟计算的值
  • 。Net下Windows服务程序开发疑惑
  • @Autowired注解的实现原理
  • @SuppressWarnings(unchecked)代码的作用
  • [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
  • [2024] 十大免费电脑数据恢复软件——轻松恢复电脑上已删除文件
  • [BIZ] - 1.金融交易系统特点