数据结构——图

一、引言

在计算机科学中,图是一种非常强大的数据结构,它能够表示复杂的对象关系。从社交网络到交通网络,从计算机网络到项目任务调度,图的应用无处不在。理解图的基本概念、表示方法以及常见算法,对于解决实际问题具有重要意义。本文将详细介绍图的基本概念、存储结构、基本操作以及一些常见的图算法,帮助大家更好地掌握这一数据结构。

二、图的基本概念

(一)定义

图(Graph)是由顶点(Vertex)集合和边(Edge)集合组成的数据结构,通常表示为 G=(V,E),其中 V 是顶点集合,E 是边集合。图可以分为以下几种类型:

  1. 无向图(Undirected Graph):图中的边没有方向,即边 (u,v) 和 (v,u) 是相同的。

  2. 有向图(Directed Graph):图中的边有方向,即边 (u,v) 和 (v,u) 是不同的。

  3. 加权图(Weighted Graph):图中的每条边都有一个权重(Weight),用于表示边的代价或距离。

(二)基本术语

  1. 顶点(Vertex):图中的一个节点,表示一个实体。

  2. 边(Edge):连接两个顶点的线,表示两个实体之间的关系。

  3. 度(Degree)

    • 在无向图中,一个顶点的度是指与该顶点相连的边的数量。

    • 在有向图中,一个顶点的度分为入度(In - Degree)和出度(Out - Degree)。入度是指指向该顶点的边的数量,出度是指从该顶点出发的边的数量。

  4. 路径(Path):从一个顶点到另一个顶点的边的序列。

  5. 简单路径(Simple Path):路径中不包含重复的顶点。

  6. 环(Cycle):起点和终点相同的路径。

  7. 连通图(Connected Graph):在无向图中,任意两个顶点之间都存在路径。

  8. 定义:使用一个二维数组来表示图的顶点之间的关系。对于无向图,如果顶点 u 和顶点 v 之间有边,则 matrix[u][v]=1(或权重值),否则为 0。对于有向图,矩阵的行表示出发顶点,列表示到达顶点。

    • 强连通图(Strongly Connected Graph):在有向图中,任意两个顶点之间都存在双向路径。

    • 子图(Subgraph):由原图的一部分顶点和边组成的图。

    • 生成树(Spanning Tree):一个连通图的生成树是一个包含图中所有顶点的无环连通子图。

三、图的存储结构

(一)邻接矩阵(Adjacency Matrix)

  1. 定义:使用一个二维数组来表示图的顶点之间的关系。对于无向图,如果顶点 u 和顶点 v 之间有边,则 matrix[u][v]=1(或权重值),否则为 0。对于有向图,矩阵的行表示出发顶点,列表示到达顶点。

  2. 优点

    • 简单直观,容易实现。

    • 适合稠密图(边数较多的图)。

  3. 缺点

    • 空间复杂度高,对于稀疏图(边数较少的图)非常浪费空间。

    • 遍历邻接点时效率较低。

二)邻接表(Adjacency List)

  1. 定义:使用一个数组存储所有顶点,每个顶点对应一个链表,链表中存储与该顶点相邻的顶点。

  2. 优点

    • 空间复杂度低,适合稀疏图。

    • 遍历邻接点时效率较高。

  3. 缺点

    • 实现相对复杂,需要管理链表。

(三)边表(Edge List)

  1. 定义:使用一个数组或链表存储图中的所有边。每条边包含两个顶点(对于无向图)或两个顶点和方向(对于有向图)。

  2. 优点

    • 实现简单,适合边的操作。

  3. 缺点

    • 遍历邻接点时效率较低,需要遍历整个边表。

四、图的基本操作

(一)创建图

以下是使用邻接表创建图的C语言代码示例:

#include <stdio.h>
#include <stdlib.h>// 定义图的顶点结构
typedef struct Vertex {int id;                   // 顶点编号struct Edge* edges;       // 指向边的链表
} Vertex;// 定义图的边结构
typedef struct Edge {int target;               // 边的目标顶点编号struct Edge* next;        // 指向下一个边
} Edge;// 定义图结构
typedef struct Graph {int num_vertices;         // 顶点数量Vertex* vertices;         // 顶点数组
} Graph;// 创建图
Graph* create_graph(int num_vertices) {Graph* graph = (Graph*)malloc(sizeof(Graph));graph->num_vertices = num_vertices;graph->vertices = (Vertex*)malloc(num_vertices * sizeof(Vertex));for (int i = 0; i < num_vertices; i++) {graph->vertices[i].id = i;graph->vertices[i].edges = NULL;}return graph;
}// 添加边(无向图)
void add_edge(Graph* graph, int src, int dest) {// 添加从 src 到 dest 的边Edge* edge1 = (Edge*)malloc(sizeof(Edge));edge1->target = dest;edge1->next = graph->vertices[src].edges;graph->vertices[src].edges = edge1;// 添加从 dest 到 src 的边(无向图)Edge* edge2 = (Edge*)malloc(sizeof(Edge));edge2->target = src;edge2->next = graph->vertices[dest].edges;graph->vertices[dest].edges = edge2;
}// 释放图的内存
void free_graph(Graph* graph) {for (int i = 0; i < graph->num_vertices; i++) {Edge* edge = graph->vertices[i].edges;while (edge != NULL) {Edge* temp = edge;edge = edge->next;free(temp);}}free(graph->vertices);free(graph);
}

(二)遍历图

  1. 深度优先搜索(DFS)

void dfs(Graph* graph, int vertex, int visited[]) {visited[vertex] = 1;printf("%d ", vertex);Edge* edge = graph->vertices[vertex].edges;while (edge != NULL) {if (!visited[edge->target]) {dfs(graph, edge->target, visited);}edge = edge->next;}
}void dfs_traversal(Graph* graph) {int visited[graph->num_vertices] = {0};for (int i = 0; i < graph->num_vertices; i++) {if (!visited[i]) {dfs(graph, i, visited);}}
}

 广度优先搜索(BFS)

#include <queue.h> // 假设有一个简单的队列库void bfs(Graph* graph, int start) {int visited[graph->num_vertices] = {0};Queue* queue = create_queue();enqueue(queue, start);visited[start] = 1;while (!is_queue_empty(queue)) {int vertex = dequeue(queue);printf("%d ", vertex);Edge* edge = graph->vertices[vertex].edges;while (edge != NULL) {if (!visited[edge->target]) {enqueue(queue, edge->target);visited[edge->target] = 1;}edge = edge->next;}}free_queue(queue);
}

五、常见的图算法

(一)最短路径算法

  1. Dijkstra算法:用于计算单源最短路径,适用于加权图且边的权重为非负数。

void floyd(Graph* graph) {int dist[graph->num_vertices][graph->num_vertices];for (int i = 0; i < graph->num_vertices; i++) {for (int j = 0; j < graph->num_vertices; j++) {if (i == j) {dist[i][j] = 0;} else {dist[i][j] = INT_MAX;}}}for (int i = 0; i < graph->num_vertices; i++) {Edge* edge = graph->vertices[i].edges;while (edge != NULL) {dist[i][edge->target] = 1;edge = edge->next;}}for (int k = 0; k < graph->num_vertices; k++) {for (int i = 0; i < graph->num_vertices; i++) {for (int j = 0; j < graph->num_vertices; j++) {if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX && dist[i][k] + dist[k][j] < dist[i][j]) {dist[i][j] = dist[i][k] + dist[k][j];}}}}for (int i = 0; i < graph->num_vertices; i++) {for (int j = 0; j < graph->num_vertices; j++) {if (dist[i][j] == INT_MAX) {printf("从 %d 到 %d 没有路径\n", i, j);} else {printf("从 %d 到 %d 的最短距离是 %d\n", i, j, dist[i][j]);}}}
}

Floyd算法:用于计算所有顶点对之间的最短路径,适用于加权图。

 

void floyd(Graph* graph) {int dist[graph->num_vertices][graph->num_vertices];for (int i = 0; i < graph->num_vertices; i++) {for (int j = 0; j < graph->num_vertices; j++) {if (i == j) {dist[i][j] = 0;} else {dist[i][j] = INT_MAX;}}}for (int i = 0; i < graph->num_vertices; i++) {Edge* edge = graph->vertices[i].edges;while (edge != NULL) {dist[i][edge->target] = 1;edge = edge->next;}}for (int k = 0; k < graph->num_vertices; k++) {for (int i = 0; i < graph->num_vertices; i++) {for (int j = 0; j < graph->num_vertices; j++) {if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX && dist[i][k] + dist[k][j] < dist[i][j]) {dist[i][j] = dist[i][k] + dist[k][j];}}}}for (int i = 0; i < graph->num_vertices; i++) {for (int j = 0; j < graph->num_vertices; j++) {if (dist[i][j] == INT_MAX) {printf("从 %d 到 %d 没有路径\n", i, j);} else {printf("从 %d 到 %d 的最短距离是 %d\n", i, j, dist[i][j]);}}}
}

六、总结

图是一种非常强大的数据结构,它能够表示复杂的对象关系。本文介绍了图的基本概念、存储结构、基本操作以及一些常见的图算法,如深度优先搜索、广度优先搜索、最短路径算法和最小生成树算法。通过这些内容,读者可以对图有一个初步的了解,并能够在实际编程中应用这些知识。希望本文能够帮助大家更好地掌握图这一数据结构。


以上内容为图的介绍和实现代码,希望对大家有所帮助。欢迎随时留言讨论!

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

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

    相关文章

    Jetbrains IDE http客户端使用教程

    简介 JetBrains IDE&#xff08;如IntelliJ IDEA&#xff0c; WebStorm&#xff0c; PhpStorm和PyCharm&#xff09;自带一个内置的HTTP客户端&#xff0c;允许直接从IDE发送HTTP请求&#xff0c;而无需使用第三方工具&#xff0c;如Postman或cURL。 JetBrains IDE 中的 HTTP…

    android手机安装deepseek-r1:1.5b

    序 本文主要展示一下如何在android手机上安装deepseek-r1:1.5b 步骤 安装termux 到https://termux.dev/cn/index.html去下载 然后执行termux-setup-storage以获取手机存储权限 安装构建依赖 pkg install git cmake golang下载ollama git clone --depth 1 https://gitee.…

    U3D支持webgpu阅读

    https://docs.unity3d.com/6000.1/Documentation/Manual/WebGPU-features.html 这里看到已经该有的差不多都有了 WOW VFX更是好东西 https://unity.com/cn/features/visual-effect-graph 这玩意儿化简了纯手搓一个特效的流程 如果按原理说就是compute shader刷position&#…

    PWM波形输出

    一、想要达到的效果 二、实现代码 因为是在1khz的频率下&#xff0c;所以用重新配置定时器0&#xff0c;定时长度为100微妙 void Timer0Init(void) //100微秒12.000MHz {AUXR | 0x80; //定时器时钟1T模式TMOD & 0xF0; //设置定时器模式TL0 0x50; //设置定时初值TH0 …

    Spring Boot整合MQTT

    MQTT是基于代理的轻量级的消息发布订阅传输协议。 1、下载安装代理 进入mosquitto下载地址&#xff1a;Download | Eclipse Mosquitto&#xff0c;进行下载&#xff0c;以win版本为例 下载完成后&#xff0c;在本地文件夹找到下载的代理安装文件 使用管理员身份打开安装 安装…

    前端 CSS 动态设置样式::class、:style 等技巧详解

    一、:class 动态绑定类名 v-bind:class&#xff08;缩写为 :class&#xff09;可以动态地绑定一个或多个 CSS 类名。 1. 对象语法 通过对象语法&#xff0c;可以根据条件动态切换类名。 <template><div :class"{ greenText: isActive, red-text: hasError }&…

    TCP三次握手全方面详解

    文章目录 (1) 三次握手各状态CLOSE状态SYN_SENT状态SYN_RECV状态ESTABLISHED状态 (2) 为什么握手时的seqnum是随机值&#xff0c;以及acknum的功能(3) 三次握手中的半连接队列&#xff08;SYN队列&#xff09;和全连接队列&#xff08;ACCEPT队列&#xff09;半连接队列全连接队…

    基于Java的远程视频会议系统(源码+系统+论文)

    第一章 概述 1.1 本课题的研究背景 随着人们对视频和音频信息的需求愈来愈强烈&#xff0c;追求远距离的视音频的同步交互成为新的时尚。近些年来&#xff0c;依托计算机技术、通信技术和网络条件的发展&#xff0c;集音频、视频、图像、文字、数据为一体的多媒体信息&#xff…

    Docker Desktop安装到其他盘

    Docker Desktop 默认安装到c盘&#xff0c;占用空间太大了&#xff0c;想给安装到其他盘&#xff0c;网上找了半天的都不对 正确安装命令&#xff1a; start /w "" "Docker Desktop Installer.exe" install --installation-dirF:\docker命令执行成功&am…

    手动配置IP

    手动配置IP&#xff0c;需要考虑四个配置项&#xff1a; 四个配置项 IP地址、子网掩码、默认网关、DNS服务器 IP地址&#xff1a;格式表现为点分十进制&#xff0c;如192.168.254.1 子网掩码&#xff1a;用于区分网络位和主机位 【子网掩码的二进制表达式一定是连续的&#…

    Qt:常用控件

    目录 控件概述 控件体系的发展 按钮类控件 QPushButton QRadioButton QCheckBox QToolButton 显示类控件 QLabel QLCDNumber QProgressBar QCalendarWidget 输入类控件 QLineEdit QTextEdit QComboBox QSpinBox QDateEdit & QTimeEdit QDial QSlider …

    【漫话机器学习系列】087.常见的神经网络最优化算法(Common Optimizers Of Neural Nets)

    常见的神经网络优化算法 1. 引言 在深度学习中&#xff0c;优化算法&#xff08;Optimizers&#xff09;用于更新神经网络的权重&#xff0c;以最小化损失函数&#xff08;Loss Function&#xff09;。一个高效的优化算法可以加速训练过程&#xff0c;并提高模型的性能和稳定…

    4G核心网的演变与创新:从传统到虚拟化的跨越

    4G核心网 随着移动通信技术的不断发展&#xff0c;4G核心网已经经历了从传统的硬件密集型架构到现代化、虚拟化网络架构的重大转型。这一演变不仅提升了网络的灵活性和可扩展性&#xff0c;也为未来的5G、物联网&#xff08;LOT&#xff09;和边缘计算等技术的发展奠定了基础。…

    拉格朗日插值法的matlab实现

    一、基本原理 比如有如下这些点 x1x2x3x4y1y2y3y4 那么在拉个朗日原理中可以把过这些点的曲线表示为&#xff1a; 其g(x)y叫做一个插值基函数&#xff08;开关&#xff09;&#xff0c;当xx1时&#xff0c;g1(x)1&#xff0c;而当xx2,x3,x4时&#xff0c;g1(x)都为0&#xf…

    使用WebStorm开发Vue3项目

    记录一下使用WebStorm开发Vu3项目时的配置 现在WebStorm可以个人免费使用啦&#xff01;?? 基本配置 打包工具&#xff1a;Vite 前端框架&#xff1a;ElementPlus 开发语言&#xff1a;Vue3、TypeScript、Sass 代码检查&#xff1a;ESLint、Prettier IDE&#xff1a;WebSt…

    35~37.ppt

    目录 35.张秘书-《会计行业中长期人才发展规划》 题目​ 解析 36.颐和园公园&#xff08;25张PPT) 题目​ 解析 37.颐和园公园&#xff08;22张PPT) 题目 解析 35.张秘书-《会计行业中长期人才发展规划》 题目 解析 插入自定义的幻灯片&#xff1a;新建幻灯片→重用…

    day44 QT核心机制

    头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QLabel> //标签类头文件 #include<QPushButton> //按钮类头文件 #include<QLineEdit> //行编辑器类头文件QT_BEGIN_NAMESPACE namespace Ui { class Widget; } …

    kafka服务端之副本

    文章目录 概述副本剖析失效副本ISR的伸缩LWLEO与HW的关联LeaderEpoch的介入数据丢失的问题数据不一致问题Leader Epoch数据丢失数据不一致 kafka为何不支持读写分离 日志同步机制可靠性分析 概述 Kafka中采用了多副本的机制&#xff0c;这是大多数分布式系统中惯用的手法&…

    [笔记] 汇编杂记(持续更新)

    文章目录 前言举例解释函数的序言函数的调用栈数据的传递 总结 前言 举例解释 // Type your code here, or load an example. int square(int num) {return num * num; }int sub(int num1, int num2) {return num1 - num2; }int add(int num1, int num2) {return num1 num2;…

    mysql8.0使用MHA实现高可用

    一、环境配置 本实验环境共有四个节点&#xff0c; 其角色分配如下&#xff08;实验机器均为centos 7.x &#xff09; 机器名称IP配置服务角色备注manager192.168.8.145manager控制器用于监控管理master192.168.8.143数据库主服务器开启bin-log relay-log 关闭relay_logslave…