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

图----无向图

1.定义

图的定义:图是由一组顶点和一组能够将两个顶点相连的边组成

边:edge

顶点:vertex

 连通图:如果从任意一个顶点都存在一条路径到达另外一个任意顶点,我们称这幅图是连通图。

非连通图:由若干连通的部分组成,他们都是其极大连通子图。

自环:即一条连接一个顶点和其自身的边

平行边:连接同一顶点的两条边称为平行边

 图的密度:是指已经连接的顶点对占所有可能被连接的顶点对的比例。在稀疏图中,被连接的顶点对很少;而在稠密图中,只有少部分顶点对之间没有边连接。

二分图:是一种能够将所有结点分为两部分的图,其中图的每条边所连接的两个顶点都分别属于不同的部分。

2.图的不同表示方法

2.1邻接表数组表示

用一个以顶点为索引的列表数组,其中的每个元素都是和该顶点相邻的定点列表

 2.2邻接矩阵 表示

用一个V乘以V的布尔矩阵。当顶点V和顶点W之间有相连的边时,定义V行W列的元素值为true,否则为false。如果含有上百万个顶点,V的平方个布尔值所需要的空间会非常大。

3.相关API及代码

package com.sid.graph;public class Vertex {String value;//顶点的值Edge firstEdge;//第一条边int index;//在外层数组的下标public Vertex(String value, Edge firstEdge,int index) {super();this.value = value;this.firstEdge = firstEdge;this.index = index;}
}
package com.sid.graph;public class Edge {Vertex vertex;//该边对应的顶点int weight;//权重,无向图,有向图权重为0Edge next;//下一个边/*** 构建一条边 weight未0表示无向图或者有向图 不为0表示网* @param vertex* @param weight* @param next*/public Edge(Vertex vertex, int weight, Edge next) {super();this.vertex = vertex;this.weight = weight;this.next = next;}
}
package com.sid.graph;import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class Graph {/*** 顶点数*/private final int V;/*** 边数*/private int E;/*** 邻接表*/private List<Vertex> adj;public Graph(int V) {this.V = V;this.E = 0;adj = new ArrayList<Vertex>(V);  /** 创建邻接表 */}public int V() {return V;}public int E() {return E;}/*** 插入顶点*/public Vertex addVertex(String value) {int size = adj.size();Vertex vertex = new Vertex(value, null,size);adj.add(vertex);return vertex;}/*** 获取顶点*/public Vertex getVertexByValue(String value) {for (int i = 0; i < V; i++) {if (adj.get(i).value.equals(value)) {return adj.get(i);}}return null;}/*** 获取顶点*/public Vertex getVertexByIndex(int index) {return adj.get(index);}/*** 添加无向图的边* @param vertex1 第一个顶点* @param vertex2 第二个顶点*/public void addUndigraphEdge(Vertex vertex1,Vertex vertex2) {//因为是无向图,所以就直接加入addEdgeByVertex(vertex1,vertex2,0);addEdgeByVertex(vertex2,vertex1,0);}/*** 添加有向图的边* @param start 开始节点* @param end 结束节点*/public void addDigraphEdge(Vertex start,Vertex end) {//因为是有向图,所以只有一条边addEdgeByVertex(start,end,0);}/*** 添加一条由start-->end的边** @param start* @param end* @param weight 权重未0表示无向图或者有向图,部位0表示网*/private void addEdgeByVertex(Vertex start, Vertex end, int weight) {//1、找到第一个顶点Vertex v1 = this.getVertexByValue(start.value);if(v1 == null){v1 = addVertex(start.value);}//2、检查这条边是否已经存在,已经存在就直接报错for (Vertex w : adj(v1)) {if (end.value.equals(w.value)) {System.out.println("边" + start.value + "-->" + end.value + "已经加入,不可以再加入");return;}}//3.添加Edge firstEdge = v1.firstEdge;if (firstEdge == null) {firstEdge = new Edge(end, weight, null);v1.firstEdge = firstEdge;} else {//当前节点变为第一个节点Edge nowEdge = new Edge(end, weight, null);v1.firstEdge = nowEdge;nowEdge.next = firstEdge;}E++;}/*** 计算V节点的度数*/public static int degree(Graph g, int v) {int degree = 0;Edge firstEdge = g.getVertexByIndex(v).firstEdge;while (firstEdge != null) {degree++;firstEdge = firstEdge.next;}return degree;}/*** 计算所有顶点的最大度数*/public static int maxDegree(Graph G) {int max = 0;for (int v = 0; v < G.V(); v++) {if (degree(G, v) > max) {max = degree(G, v);}}return max;}/*** 计算所有顶点的平均度数*/public static int avgDegree(Graph G) {return 2 * G.E() / G.V();}/*** 计算自环的的个数*/public static int numberOfSelfLoops(Graph g) {int count = 0;for (int v = 0; v < g.V(); v++) {for(int w : g.adj(v)){if(v == w){count++;}}}return count / 2;   //每条边都被记过两次}/*** 得到跟V节点相邻的所有节点*/public static List<Vertex> adj(Vertex vertex) {List result = new LinkedList();Edge firstEdge = vertex.firstEdge;while (firstEdge != null) {result.add(firstEdge.vertex);firstEdge = firstEdge.next;}return result;}/*** 得到跟V节点相邻的所有节点下标  入参是节点的数组下标*/public List<Integer> adj(int index) {List result = new ArrayList();Edge firstEdge = this.getVertexByIndex(index).firstEdge;while (firstEdge != null) {result.add(firstEdge.vertex.index);firstEdge = firstEdge.next;}return result;}/*** 图的邻接表的字符串表示*/public String toString() {String s = V + " vertices, " + E + " edges\n";for (int v = 0; v < V; v++) {Vertex vertex = this.getVertexByIndex(v);s += vertex.value + ": ";for (Vertex w : adj(vertex)) {s += w.value + "";}s += "\n";}return s;}
}

4.深度优先搜索

4.1 找到以起点s连通的所有顶点,和个数 

实现:

用一个递归方法来遍历所有顶点,在访问一个顶点时:

将它标记为已访问

递归地访问它的所有没有被标记过的邻居顶点

package com.sid.graph;public class DepthFirstSearch {private boolean[] marked;private int count;public DepthFirstSearch(Graph G,int s){marked = new boolean[G.V()];dfs(G,s);}private void dfs(Graph G, int index) {marked[index] = true;count++;for(int w : G.adj(index)){if(!marked[w]){dfs(G,w);}}}public boolean marked(int w ){return marked[w];}public int count(){return count;}
}

4.2深度优先搜索查找图中路径

注意这不一定是最短路径,比如说下面这个例子,查出来的0到1的路径,不是0----1,而是0----2----1 

         

package com.sid.graph;import java.util.Stack;public class DepthFirstPaths {private boolean[] marked;  //这个顶点上调用过dfs()了吗private int[] edgeTo;       //从起点到一个顶点的已知路径上的最后一个顶点  比如S---A---C 则 edgeTo[C]=A  edgeTo[C]其实表示的是谁直接指向C点private final int s;       //起点public DepthFirstPaths(Graph G, int s) {marked = new boolean[G.V()];edgeTo = new int[G.V()];this.s = s;dfs(G, s);}private void dfs(Graph G, int index) {marked[index] = true;for (int w : G.adj(index)) {if (!marked[w]) {edgeTo[w] = index;dfs(G, w);}}}public boolean hasPathTo(int v){  //起点S是否有路径到Vreturn marked[v];}public Iterable<Integer> pathTo(int v){if(!hasPathTo(v)){return null;}Stack<Integer> path = new Stack<Integer>();for(int x = v ; x != s ; x = edgeTo[x]){path.push(x);}path.push(s);return path;}
}

5.广度优先搜索

深度优先搜索得到的路径不仅取决于图的结构,还取决于图的表示和递归调用的性质。

“从S到给定目标的顶点V是否存在一条路径?如果有,找出其中最短的那条”,则需要用广度优先搜索。

实现:

1.使用一个队列来保存所有已经被标记过,但其邻接表还未被检查过的顶点

2.先将起点加入队列,然后重复以下步骤直到队列为空

        取队列中的下一个顶点v并标记它

        将与v相邻的所有未被标记过的顶点加入队列

package com.sid.graph;import java.util.*;public class BreadthFirstPaths {private boolean[] marked;  //这个顶点上调用过dfs()了吗private int[] edgeTo;       //从起点到一个顶点的已知路径上的最后一个顶点  比如S---A---C 则 edgeTo[C]=A  edgeTo[C]其实表示的是谁直接指向C点private final int s;       //起点public BreadthFirstPaths(Graph G, int s) {marked = new boolean[G.V()];edgeTo = new int[G.V()];this.s = s;bfs(G, s);}private void bfs(Graph G, int s) {Queue<Integer> queue = new PriorityQueue<>();marked[s] = true;queue.add(s);while (!queue.isEmpty()){int v = queue.poll();for(int w : G.adj(v)){if(!marked[w]){edgeTo[w] = v;marked[w] = true;queue.add(w);}}}}public boolean hasPathTo(int v){return marked[v];}public Iterable<Integer> pathTo(int v){if(!hasPathTo(v)){return null;}Stack<Integer> path = new Stack<Integer>();for(int x = v ; x != s ; x = edgeTo[x]){path.push(x);}path.push(s);return path;}
}

6.连通分量

无向图G的极大连通子图称为G的连通分量( Connected Component)。

任何连通图的连通分量只有一个,即是其自身,非连通的无向图有多个连通分量。 

实现

使用marked[]数组来寻找一个顶点作为每个连通分量中深度优先搜索的起点。

递归的深度优先搜索第一次调用的参数是顶点0,它会标记所有与0连通的顶点。

然后构造函数中for循环会查找每个没有被标记的顶点,并且递归调用dfs()来标记和它相邻的所有顶点。

使用一个以顶点作为索引的数组id[],将同一个连通分量的顶点和连通分量的标识符关联起来(int 值)。这个数组是的connected()方法的实现变得十分简单。

标识符0会被赋予第一个连通分量中的所有顶点,1会被赋予第二个连通分量中的所有顶点,以此类推。这样所有的标识符都会如API中指定的那样在0到count()-1之间。这个约定使得以子图作为索引的数组成为可能。

例子

最后

id[0]= 0

id[1]= 0

id[2]= 0

id[3]= 0

id[4]= 0

id[5]= 0

id[6]= 0

id[7]= 1

id[8]= 1

id[9]= 2

id[10]= 2

id[11]= 2

id[12]= 2

也就是说count[]的值相同的是一个连通分量,count[]的值不同的节点之间是走不通的。

代码

package com.sid.graph;public class CC {private boolean[] marked;private int[] id;private int count;public CC(Graph G){marked = new boolean[G.V()];id = new int[G.V()];for(int s = 0 ; s < G.V(); s++){if(!marked[s]){dfs(G,s);count++;}}}private void dfs(Graph G,int v){marked[v] = true;id[v] = count;for(int w : G.adj(v)){if(!marked[w]){dfs(G,w);}}}public boolean connected(int v, int w){return id[v] == id[w];}public int id(int v){return id[v];}public int count(){return count;}
}

7.G是无环图吗

使用深度优先处理

如果不存在自环和平行边,就是无环图

package com.sid.graph;public class Cycle {private boolean[] marked;private boolean hasCycle;public Cycle(Graph G){marked = new boolean[G.V()];for(int s = 0 ; s < G.V(); s++){if(!marked[s]){dfs(G,s,s);}}}private void dfs(Graph G, int v , int u){marked[v] = true;for(int w : G.adj(v)){if(!marked[w]){dfs(G,w,v);}else if(w != u){  //A---B 与B相邻的节点肯定有A,我理解的是无向图中他们属于同一条边,不是环。hasCycle = true;}}}public boolean hasCycle(){return hasCycle;}
}

8.G是二分图吗(双色问题)

package com.sid.graph;public class TwoColor {private boolean[] marked;private boolean[] color;private boolean isTowColorable = true;public TwoColor(Graph G){marked = new boolean[G.V()];color = new boolean[G.V()];for(int s = 0 ; s < G.V(); s++){if(!marked[s]){dfs(G,s);}}}private void dfs(Graph G, int v) {marked[v] = true;for (int w : G.adj(v)){if(!marked[w]){color[w] = !color[v];}else if(color[w] == color[v]){isTowColorable = false;return;}}}public boolean isBipartite(){return isTowColorable;}
}

9.符号图

节点里面装的不是数字,而是其他的,比如字符串

 

 

 

 

相关文章:

图----无向图

1.定义 图的定义&#xff1a;图是由一组顶点和一组能够将两个顶点相连的边组成 边&#xff1a;edge 顶点&#xff1a;vertex 连通图&#xff1a;如果从任意一个顶点都存在一条路径到达另外一个任意顶点&#xff0c;我们称这幅图是连通图。 非连通图&#xff1a;由若干连通的…...

【C++1】函数重载,类和对象,引用,/string类,vector容器,类继承和多态,/socket,进程信号

文章目录1.函数重载&#xff1a;writetofile()&#xff0c;Ctrue和false&#xff0c;C0和非02.类和对象&#xff1a;vprintf构造函数&#xff1a;对成员变量初始化析构函数&#xff1a;一个类只有一个&#xff0c;不允许被重载3.引用&#xff1a;C中&取地址&#xff0c;C中…...

JetpackCompose从入门到实战学习笔记8—ConstraintLayout的简单使用

JetpackCompose从入门到实战学习笔记8—ConstraintLayout的简单使用 1.简介&#xff1a; Compose 中的 ConstraintLayout ConstraintLayout 是一种布局&#xff0c;让您可以相对于屏幕上的其他可组合项来放置可组合项。它是一种实用的替代方案&#xff0c;可代替使用多个已嵌…...

Spring Boot 快速入门(绝对经典)

目录 1、理论概述 1.1、什么是Spring Boot? 1.2、Spring Boot的特点 1.3、开发环境 2、实战——创建和配置项目 2.1、Spring Boot项目创建的两种方式 2.1.1、方法一&#xff1a;通过网站构建项目 2.1.2、使用Spring Initializr创建&#xff08;推荐&#xff09; 2.2、…...

golang context上下文

文章目录一、为什么需要context二、context 接口三、Background 方法四、 with 系列函数1、WithCancel 方法2、WithDeadline 方法3、WithTimeout 方法4、WithValue 方法五、使用注意事项一、为什么需要context 在 Go http包的Server中&#xff0c;每一个请求在都有一个对应的 …...

Linux---Linux是什么

Linux 便成立的核心网站&#xff1a; http://www.kernel.org Linux是什么 Linux 就是一套操作系统 Linux 就是核心与系统呼叫接口那两层 软件移植&#xff1a;如果能够参考硬件的功能函数并据以修改你的操作系统程序代码&#xff0c; 那经过改版后的操作系统就能够在另一个硬…...

C语言(Tgmath.h库(C99),exit和atexit)

一.Tgmath.h库&#xff08;C99&#xff09; C99标准提供得tgmath.h头文件定义了泛型类型宏。比如在math.h中为一个函数定义了3中类型(float,double和long double)的版本&#xff0c;那么tgmath.h文件就创建一个泛型类型宏&#xff0c;与原来的float,double和long double版本的…...

LeetCode 刷题系列 -- 739. 每日温度

给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。示例 1:输入:temperatures …...

如何生成毕业论文的目录和创建模板

有粉丝同学最近在写毕业论文&#xff0c;其中比较让人恼火的是毕业论文的目录&#xff0c;折腾了几遍没弄好&#xff0c;想让我写个简单地教程&#xff0c;那就来吧。主要分为三步&#xff1a;第一步是从模板里面提取标题的样式&#xff0c;第二步是对自己的论文使用设置好的标…...

新来的23岁软件测试员上来秀了波操作,把几个老员工看傻了

春招了&#xff0c;公司来了个小伙子&#xff0c;一看简历&#xff0c;嘿&#xff1f;22岁&#xff0c;这不刚毕业的小毛孩子嘛&#xff0c;结果没想到人家上来就把现有项目的性能优化了一遍&#xff0c;给公司节省了一半的成本&#xff0c;这种“王炸”打法&#xff0c;直接给…...

Window10开放某个端口

需求&#xff1a;由于防火墙原因&#xff0c;开放某个端口:如9999 在开始那里搜索防火墙-进入防火墙 第一步&#xff1a;核实是否启动了防火墙&#xff0c;之后进行 第二步&#xff1a;点击“高级设置”&#xff0c;→“入站规则”→“新建规则”→“端口”→ “下一步” …...

进阶7 分页查询

进阶7 分页查询&#xff01;&#xff01;&#xff01; 目录概述练习题概述 应用场景&#xff1a;当要显示的数据一页显示不全&#xff0c;需要分页提交SQL请求 语法&#xff1a; select 查询列表 from 表名 【join type join 表2 on 连接条件 where 筛选条件 group by 分组字段…...

利用升序定时器链表处理非活动连接

参考自游双《Linux高性能服务器编程》 背景 服务器同常需要定期处理非活动连接&#xff1a;给客户发一个重连请求&#xff0c;或关闭该连接&#xff0c;或者其他。我们可以通过使用升序定时器链表处理非活动连接&#xff0c;下面的代码利用alarm函数周期性的触发SIGALRM信号&a…...

MySQL 开发规范

一、数据库命名规范所有数据对象名称必须小写 :​​db_user​​禁止使用MySQL 保留关键字&#xff0c;若是则引用 临时表以​​tmp_​​​ 开头&#xff0c;备份表以​​bak_​​ 开头并以时间戳结尾所有存储相同数据的列名和列类型必须一致二、数据库基本设计规范​​1、MySQL…...

【C语言进阶】预处理与程序环境

目录一.详解编译与链接1.前言2.翻译环境3.剖析编译过程4.运行环境二.预处理详解1.预定义符号2.剖析#define(1).定义标识符(2).定义宏(3).替换规则(4).#和##(5).宏与函数的对比(6).#undef3.条件编译4.文件包含(1).头文件包含的方式(2).嵌套文件包含一.详解编译与链接 1.前言 在…...

【Docker知识】将环境变量传递到容器

一、说明 程序通常通过与软件捆绑在一起的配置来控制操作&#xff0c;环境变量允许用户在运行时设置它们。但是&#xff0c;在 Docker 容器中运行进程会使事情变得复杂&#xff0c;那么如何将环境变量传递给容器呢&#xff1f;下面介绍若干个传递办法。 二、环境变量有何用途 环…...

Allegro如何更改铜皮显示密度操作指导

Allegro如何更改铜皮显示密度操作指导 用Allegro做PCB设计的时候,铜皮正常显示模式如下图 铜皮的密度是基本填充满的,Allegro支持更改铜皮的显示密度 如下图 如何更改密度,具体操作如下 点击setup...

ThinkPHP5酒店预订管理系统

有需要请私信或看评论链接哦 可远程调试 ThinkPHP5酒店预订管理系统一 介绍 此酒店预订管理系统基于ThinkPHP5框架开发&#xff0c;数据库mysql&#xff0c;采用了ueditor富文本编辑器。系统角色分为用户&#xff0c;员工和管理员。用户可注册登录并预订酒店和评论等&#xff…...

【MySQL】MyCat分库分表分片规则配置详解与实战(MySQL专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建工设优化。文章内容兼具广度深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于知名金融公…...

OpenWrt路由器设置域名动态解析手把手教程

文章目录0、前言1、准备工作2、详细步骤2.1、OpenWrt路由器软件包安装2.2、防火墙放行入站数据&#xff08;修改为“接受”并保存应用&#xff09;2.3、域名解析服务商对域名的解析设置2.4、路由器中动态域名插件的设置0、前言 因为一直用着内网穿透&#xff08;zerotier或者是…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...