数据结构--》连接世界的无限可能—— 图

news2024/12/23 11:56:47

        图作为数据结构中的一种重要概念,扮演着连接世界的纽带。与树和二叉树相比,图更加灵活和多样化,它能够描述各种实际问题中的复杂关系,如社交网络中的人际联系、城市交通中的路线规划以及电子网络中的通信路径等。

        无论你是初学者还是进阶者,本文将为你提供简单易懂、实用可行的知识点,帮助你更好地掌握图在数据结构和算法中的重要性,进而提升算法解题的能力。接下来让我们开启数据结构与算法的奇妙之旅吧。

目录

图的基本概念

图的存储表示

图的基本操作

图的遍历与连通性

最小生成树 

最短路径问题

关键路径问题


图的基本概念

图是由一组顶点(节点)和连接这些顶点的边(关系)组成的非线性数据结构。图用于表示元素之间的关系,通过节点和边来描述图中元素之间的连接情况。

图的定义包括以下几个要素

顶点:图中的节点表示实体或对象,通常用圆圈或方框来表示。每个节点可以包含一些相关信息,如名称、属性等。

:图中的边表示顶点之间的关系,用于连接不同的顶点。边可以是有向的(箭头表示方向)或无向的(不带箭头表示),也可以带有权重(表示边的权值)。

邻接点:对于一个顶点,与它直接相连的顶点称为邻接点。邻接点之间通过边相连。

路径:路径是指顶点之间经过的一系列边的序列。路径的长度可以通过经过的边的数量来计算。

:如果路径的起点和终点是同一个顶点,并且至少经过了一条边,就形成了一个圈。圈也被称为循环。

有向图和无向图

顶点的度

对于无向图而言,顶点v的度是指依附于该顶点的边的条数,记为TD(v)。

对于有向图而言,我们要探讨的是顶点的入度和出度:

顶点v的度等于入度和出度之和,即 TD(v) = ID(v) + OD(v)。

入读是以顶点v为终点的有向边的数目,记为ID(V);出度是以顶点v为起点的有向边的数目,记为OD(v)。

顶点与顶点之间的关系描述

连通图和强连通图

子图:(研究图的局部)

对于有向图来说子图和生成子图的概念也是一样的:

连通分量

无向图中的极大连通子图称为连通分量:

有向图中的极大强连通子图称为有向图的强连通分量:

生成树

连通图的生成树是包含图中全部顶点的一个极小连通子图。

生成森林

在非连通图中,连通分量的生成树构成了非连通图的生成森林。

几种特殊形态的图

回顾重点,其主要内容整理成如下内容:

图的存储表示

图可以用多种方式进行存储表示,常见的有两种主要方法:邻接矩阵和邻接表。

邻接矩阵法

邻接矩阵是一个二维数组,用于表示图中的节点之间的连接关系。对于一个有n个节点的图,邻接矩阵是一个大小为n×n的方阵。如果节点i和节点j之间存在一条边,则邻接矩阵中第i行第j列的元素为1;否则为0。邻接矩阵的优点是可以快速判断任意两个节点之间是否有边,但缺点是当图比较稀疏时会占用大量的空间。

如果我们采用邻接矩阵法存储带权图(网):

邻接表法

根据有向无向连判断当前结点所连接的下一个结点的索引:

回顾重点,其主要内容整理成如下内容: 

十字链表法存储有向图

邻接多重表存储无向图

如果删除结点和边的话得到的相应结果如下:

回顾重点,其主要内容整理成如下内容:

图的基本操作

如果我们想知道两个结点之间是否存在边可以通过邻接矩阵或者邻接表的方式:

如果我们想在图中插入顶点x,可以采用如下的方式进行:

如果想在图中删除某个顶点x,可以采用如下的方式进行:

如果想知道图中顶点x的第一个邻接点,若有则返回顶点号,若x没有邻接点或图中不存在x,则返回-1:

图的遍历与连通性

图的遍历是指访问图中所有节点的过程。通过遍历,我们可以对图的结构和节点之间的关系进行全面的了解。常用的图遍历算法包括广度优先搜索(BFS)和 深度优先搜索(DFS)。

广度优先遍历:图的广度优先遍历(Breadth-First Search,BFS)是一种从起始节点开始,先访问离起点最近的节点,然后逐层向外扩展访问的策略。它保证了先访问距离起始节点相近的节点,然后再访问距离稍远的节点。

广度优先遍历序列的案例如下:

下面是用 C 语言实现图的广度优先遍历的基本代码示例:

#include <stdio.h>

#define MAX_NODES 100

typedef struct {
    int neighbor[MAX_NODES];
    int numNeighbors;
    int visited;
} Node;

void bfs(Node graph[], int start, int numNodes) {
    int queue[MAX_NODES];
    int front = 0, rear = 0;

    // 将起始节点加入队列并标记为已访问
    queue[rear++] = start;
    graph[start].visited = 1;

    while (front != rear) {
        int current_node = queue[front++];  // 队首节点出队
        printf("%d ", current_node);  // 访问当前节点

        // 遍历当前节点的邻居节点
        for (int i = 0; i < graph[current_node].numNeighbors; i++) {
            int neighbor = graph[current_node].neighbor[i];
            if (!graph[neighbor].visited) {  // 如果邻居节点未被访问过
                queue[rear++] = neighbor;  // 将邻居节点入队
                graph[neighbor].visited = 1;  // 标记邻居节点为已访问
            }
        }
    }
}

int main() {
    // 创建一个示例图
    Node graph[MAX_NODES];

    // 初始化图中的节点和边
    for (int i = 0; i < MAX_NODES; i++) {
        graph[i].numNeighbors = 0;
        graph[i].visited = 0;
    }

    // 添加边关系
    graph[0].neighbor[graph[0].numNeighbors++] = 1;
    graph[0].neighbor[graph[0].numNeighbors++] = 2;
    graph[1].neighbor[graph[1].numNeighbors++] = 3;
    graph[2].neighbor[graph[2].numNeighbors++] = 4;
    graph[2].neighbor[graph[2].numNeighbors++] = 5;
    graph[3].neighbor[graph[3].numNeighbors++] = 6;
    graph[4].neighbor[graph[4].numNeighbors++] = 7;

    // 进行广度优先遍历
    bfs(graph, 0, 8);  // 假设图中有 8 个节点

    return 0;
}

广度优先生成树用于在一个无向图或有向图中从给定的起始节点开始,以广度优先的方式生成一个树状结构,该树包含了从起始节点出发到达所有可达节点的最短路径。 

回顾重点,其主要内容整理成如下内容:

深度优先遍历:深度优先搜索(Depth-First Search,DFS)是一种先走尽可能远的策略,即沿着当前路径走到底,直到不能再走下去为止,然后回溯到前一个节点,继续探测其他路径,直到所有节点都被访问过。

深度优先生成树是指通过深度优先搜索算法生成的一棵树状结构,该树包含了从给定起始节点出发到达所有可达节点的最短路径。

下面是用 C 语言实现深度优先遍历的基本代码示例:

#include <stdio.h>

#define MAX_NODES 100

typedef struct {
    int neighbor[MAX_NODES];
    int numNeighbors;
    int visited;
} Node;

void dfs(Node graph[], int current_node) {
    printf("%d ", current_node);  // 先访问当前节点
    graph[current_node].visited = 1;  // 标记当前节点为已访问

    // 遍历当前节点的邻居节点,递归调用 dfs 函数
    for (int i = 0; i < graph[current_node].numNeighbors; i++) {
        int neighbor = graph[current_node].neighbor[i];
        if (!graph[neighbor].visited) {  // 如果邻居节点未被访问过
            dfs(graph, neighbor);  // 递归访问邻居节点
        }
    }
}

int main() {
    // 创建一个示例图
    Node graph[MAX_NODES];

    // 初始化图中的节点和边
    for (int i = 0; i < MAX_NODES; i++) {
        graph[i].numNeighbors = 0;
        graph[i].visited = 0;
    }

    // 添加边关系
    graph[0].neighbor[graph[0].numNeighbors++] = 1;
    graph[0].neighbor[graph[0].numNeighbors++] = 2;
    graph[1].neighbor[graph[1].numNeighbors++] = 3;
    graph[2].neighbor[graph[2].numNeighbors++] = 4;
    graph[2].neighbor[graph[2].numNeighbors++] = 5;
    graph[3].neighbor[graph[3].numNeighbors++] = 6;
    graph[4].neighbor[graph[4].numNeighbors++] = 7;

    // 进行深度优先遍历
    dfs(graph, 0);  // 假设从节点 0 开始遍历

    return 0;
}

连通分量

连通分量是指一个无向图中的最大连通子图。连通分量由若干个顶点及它们之间的边组成,其中每个顶点都可以通过路径与其他顶点相互到达。

在无向图中,连通分量可以非常直观地理解为图中的一片区域,该区域中的所有顶点都可以彼此到达。每个连通分量都是图的一部分,并且一个图可以有多个连通分量。

在有向图中,连通分量的定义相对复杂一些。有向图的连通性需要考虑顶点之间的有向路径。有向图中的连通分量是指在该图中,每个顶点都存在至少一条有向路径可以到达其他所有顶点。

回顾重点,其主要内容整理成如下内容: 

最小生成树 

最小生成树是指在无向带权连通图中,寻找一棵包含所有顶点的生成树,并且该树的所有边的权值之和最小。最小生成树有以下特点:

最小生成树可能是多个;最小生成树中的边数等于顶点数减1;最小生成树中不会有回路。

常见的解决最小生成树问题的算法包括 Prim算法Kruskal算法

Prim算法(普里姆):从某一个顶点开始构建生成树;每次将代价最小的新顶点纳入生成树,直到所有的顶点都纳入为止。

这里我们选择p城为根结点,然后依次向外扩张找连线之间代价最小的结点,最终得到最小代价:

Kruskal算法(克鲁斯卡尔):每次选择一条权值最小的边,使这条边的两头连通(原本已经连通的就不选)直到所有的结点都连通。

两种算法的比较:

最短路径问题

图的最短路径问题是指在一个加权有向图或无向图中,寻找两个顶点之间最短路径的问题。最短路径可以通过边的权值和来衡量。最短路径问题主要分为以下两种情况:

单源最短路径:从给定的一个起始节点到图中其他所有节点之间的最短路径。它可以用于解决从一个固定起点到其他节点的最短路径问题。

BFS算法(无权图):

Dijkstra算法(带权图、无权图):

各顶点间的最短路径:计算图中任意两个节点之间的最短路径。它可以用于解决任意节点对之间的最短路径问题。

Floyd算法(带权图、无权图):

回顾重点,其主要内容整理成如下内容:  

关键路径问题

关键路径是指项目计划中不能延误的最长路径,它决定了整个项目的最短完成时间。关键路径问题常用于项目管理和工程规划中。

在AOE网中我们要了解以下概念:

根据上文相关例子的举出之后,接下来我们开始计算相应的数值:

求所有事件的最早发生时间:(等最慢的完成才可以)

求所有事件的最迟发生事件:(汇点的最迟发生时间与最早发生时间一致,我们以汇点为触发点你拓扑排序,即减去相应路径上发生的事件从而得到该点的最迟发生时间):

求所有活动的最早发生时间:(通过前面计算处的事件最早发生时间推算出活动最早发生时间):

求所有活动的最晚发生时间:(通过前面计算处的事件最迟发生时间减去相应活动需要的时间得出)

求所有活动的时间余量:(活动最晚发生时间减去活动最早发生时间)

注意:以下是相应的关键活动和关键路径的特性:

回顾重点,其主要内容整理成如下内容: 

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

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

相关文章

【【萌新的SOC学习之SD卡读写TXT文本实验】】

萌新的SOC学习之SD卡读写TXT文本实验 SD卡 Secure Digital Card SD卡的引脚定义 我们会用的数据脚就这几个 对于我们FPGA 其实更会倾向于选择 SPI的功能 而TF卡相对于SD卡的区别在于 SD卡只有一个电源地 这里相对于原本的SPI多了一个CD引脚 CD信号是相当于一个卡检测…

竞赛选题 深度学习+opencv+python实现车道线检测 - 自动驾驶

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

光纤激光切割机如何高效的切割铜等高反材料

高反射材料的切割过程往往具有挑战性&#xff0c;对于许多光纤激光切割设备厂商而言都是难以解决的问题。但是作为铜、铝、金等常见的高反射性材料又需要在日常生产中经常进行加工处理。 很多厂家解决的办法之一就是采用相应的辅助气体。在光纤激光切割机切割铜时&#xff0c;辅…

xshell 上传下载文件命令

Windows 和 Linux上传或下载某个文件首先你的 Linux上需要安装安装 lrzsz工具包在Linux 上执行 yum install lrzsz 上传文件&#xff1a; 输入 rz 下载文件&#xff1a;运行命令 sz zcly.tar.gz (zcly.tar.gz)为文件名称

多标签分类论文笔记 | ML-Decoder: Scalable and Versatile Classification Head

个人论文精读笔记&#xff0c;主要是翻译心得&#xff0c;欢迎旁观&#xff0c;如果有兴趣可以在评论区留言&#xff0c;我们一起探讨。 Paper: https://arxiv.org/pdf/2111.12933.pdf Code: https://github.com/Alibaba-MIIL/ML_Decoder 文章目录 0. 摘要1. 介绍2. 方法2.1 Ba…

offer

【录用通知书】 如何判断公司的好坏呢。 注意了&#xff0c;我们软件行业&#xff0c;技术管理类&#xff0c;技术类&#xff0c;产品类 好公司好企业基本都会给你说清楚&#xff0c;一项多少钱&#xff0c;加班多少钱&#xff0c;这样的 像这类公司的薪资结构复杂就要特别…

如何批量导出文件名?

如何批量导出文件名&#xff1f;在电商行业从事工作的一些同事可能经常会遇到这样的问题&#xff1a;需要将产品文件夹中的所有图片或产品名称导出到Excel工作表&#xff0c;在工作表中创建这些名称的超链接&#xff0c;并且可能会为每个产名称的后面填写一些相关信息&#xff…

LeetCode【152】乘积最大子数组

题目&#xff1a; 解析&#xff1a; 理解这个题的过程中&#xff0c;有这样的疑问&#xff1a; -2、0、2、3 子数组最大值是2*3 6&#xff0c;并非dp过程中&#xff0c;从下标0累乘的怎么办&#xff1f;这里不用担心&#xff0c;因为在dp的过程中会逐渐的求Math.max和Math.m…

知识付费小程序的推广与用户增长策略

在知识付费小程序开发完成后&#xff0c;推广和用户增长是关键的成功因素。本文将探讨一些推广策略和用户增长方法&#xff0c;并提供代码示例&#xff0c;帮助您在知识付费小程序中实施这些策略。 1. 社交媒体分享功能 在知识付费小程序中添加社交媒体分享功能&#xff0c;…

多媒体应用设计师 开始

https://www.bilibili.com/video/BV1jv411q7mz/?spm_id_from333.337.search-card.all.click&vd_source25bced4af8c6d5f851758632d0ca8444

【2023年新版】40套BIM+GIS项目案例合集,中建中铁中交企业内部学习资源免费领取

最近有很多做工程的朋友想要学习BIMGIS技术&#xff0c;向我询问相关的学习资源和资料。他们面临的普遍问题是不知道如何入门&#xff0c;找到的资料很多&#xff0c;但是很多却用不上。 为了解决大家的问题&#xff0c;我们团队花了近一个月的时间&#xff0c;精心整理了一份…

Vue3实战(05)-教你快速搭建Vue3工程化项目

除了Vue 3这个库&#xff0c;还需Vue 3 最新全家桶。 1 环境准备 之前语法演示直接使用script引入Vue 3&#xff0c;从而在浏览器里实现所有调试功能。但实际项目中&#xff0c;使用专门调试工具。在项目上线之前&#xff0c;代码也需打包压缩&#xff0c;并考虑到研发效率和…

C语言文件操作(2)

&#x1f649;本文将继续对文件操作相关知识进行讲解 1. 文件的随机读写 1.1 fseek fseek函数原型&#xff1a; fseek简而言之就是设置文件中的光标&#xff0c;stream就指向对象文件的指针&#xff0c;offset是文件中光标处相对于起始位置的偏移量&#xff0c;origin是光标所…

Docker 容器应急

容器网络简单理解 容器拥有n多张veth网卡与一张docker0网卡 docker 五种网络 bridge 默认网络&#xff0c;Docker启动后创建一个docker0网桥&#xff0c;默认创建的容器也是添加到这个网桥中。host 容器不会获得一个独立的network namespace&#xff0c;而是与宿主机共用一个…

关于Win系统提示由于找不到msvcr120.dll文件问题解决办法

在我使用电脑的过程中&#xff0c;突然弹出了一个错误提示框&#xff0c;提示我系统中缺少msvcp120.dll文件。这个文件是系统运行所必需的&#xff0c;缺少它可能会导致一些软件无法正常运行。经过一番搜索和咨询&#xff0c;我找到了以下几种解决方案&#xff0c;分享给大家&a…

【Java学习之道】文件输入输出流

引言 今天&#xff0c;我们将一起学习Java中的文件输入/输出流&#xff0c;这是许多初学者感到困惑但实际工作中经常遇到的问题。在本部分&#xff0c;我们将探讨文件输入/输出流的基本概念&#xff0c;以及如何使用Java中的类来处理文件输入/输出。 一、文件输入/输出流的概…

C语言,输出最长连号的个数

数据范围&#xff1a;0 < n <10000. 最长连号&#xff0c;就是一组数字里面出现的连续的数字的最长长度。这里的连号最长是2 3 4 5 6&#xff0c;个数为5。 要实现求连号的个数&#xff0c;就要创建一个变量&#xff0c;用来专门计算连号的个数。在不满足连号时&#xf…

shiro反序列化漏洞分析

分析源码 我们去源码里面去找找&#xff0c;搜索rememberMe&#xff1a; 发现有一个 CookieRememberMeManager 这个类&#xff0c;看名字就知道他多半就是处理 RememberMe 的逻辑&#xff0c;所以根据该类查看它干了什么 这里继承 AbstractRememberMeManager 类&#xff0c;Ab…

精益生产与MES生产管理系统相互融合

近年来&#xff0c;精益生产理念在企业管理中越来越受欢迎。它强调以最小的浪费&#xff0c;在最短的时间内&#xff0c;生产出高质量的产品。这一理念的实施手段包括准时制生产方式、适时生产方式等&#xff0c;消除浪费、看板、快换工装等都是精益提高的工具方针。 然而&…

【基础篇】四、本地部署Flink

文章目录 1、部署模式2、本地独立部署会话模式的Flink3、本地独立部署会话模式的Flink集群4、向Flink集群提交作业5、Standalone方式部署单作业模式6、Standalone方式部署应用模式的Flink 1、部署模式 会话模式&#xff08;Session Mode&#xff09; 先启动Flink集群&#xff0…