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

数据结构中无向图的邻接矩阵详解

在计算机科学的浩瀚宇宙中,数据结构无疑是那把开启高效编程大门的关键钥匙。对于计算机专业的大学生们来说,数据结构课程是专业学习路上的一座重要里程碑,而其中的图结构更是充满魅力与挑战,像一幅神秘的画卷等待我们去展开。今天,就让我们结合一段精心优化的 C 语言代码,深入探索图结构中邻接矩阵的奥秘,相信这不仅能让你在课堂学习中游刃有余,更能助你在 CSDN 等技术平台上收获知识与成长。​

一、图结构:数据世界的复杂网络​

图结构是一种比线性表和树更为复杂的数据结构,它用于表示对象之间多对多的关系。在现实生活中,图结构的应用无处不在,比如社交网络中人与人的关系、交通网络中城市与道路的连接、互联网中网页之间的链接等等。图由顶点(Vertex)和边(Edge)组成,顶点代表对象,边则表示对象之间的联系。而邻接矩阵,就是图结构众多存储方式中的一种经典方法。​

二、邻接矩阵:直观高效的存储方式​

邻接矩阵是一种用二维数组来存储图的方式。对于一个具有 ​n个顶点的图,我们可以创建一个 ​n×n的二维数组,数组中的元素 ​arcs[i][j]用于表示顶点 ​i和顶点 ​j之间是否存在边。在无向图中,如果顶点 ​i和顶点 j之间有边相连,那么 ​arcs[i][j]=arcs[j][i]=1;如果没有边相连,则 ​arcs[i][j]=arcs[j][i]=0。这种存储方式简单直观,能够快速判断两个顶点之间是否有边,对于一些需要频繁查询边信息的操作非常高效。​我们来看一段 C 语言代码,它实现了无向图邻接矩阵的创建、输出等功能。

#include <stdio.h>
#include <stdlib.h>#define MAXVEX 16                         /*顶点的最大个数*/
typedef char VexType;                     /*顶点的数据类型*/
typedef int AdjType;                      /*邻接矩阵的数据元素的类型*/// 图的邻接矩阵存储结构
typedef struct {VexType vexs[MAXVEX];                 /*顶点表,存储顶点信息*/AdjType arcs[MAXVEX][MAXVEX];         /*邻接矩阵,存储顶点之间的关系*/int n;                                /*图的当前顶点数*/
} GraphMatrix;// 创建一个无向图的邻接矩阵
GraphMatrix* CreateGraph(void) {int i, j, k, e;GraphMatrix *ga = (GraphMatrix *)malloc(sizeof(GraphMatrix));if (ga == NULL) {fprintf(stderr, "内存分配失败\n");return NULL;}// 输入顶点数量并验证do {printf("请输入顶点的个数(1-%d):", MAXVEX);if (scanf("%d", &(ga->n)) != 1 || ga->n < 1 || ga->n > MAXVEX) {printf("输入错误,请输入1-%d之间的整数\n", MAXVEX);while (getchar() != '\n');    // 清除输入缓冲区ga->n = 0;                    // 重置以继续循环} else {break;}} while (1);while (getchar() != '\n');            // 清除输入缓冲区// 输入顶点信息printf("请顺序地输入各顶点的信息(单个字符):\n");for (i = 0; i < ga->n; i++) {printf("顶点 %d: ", i);ga->vexs[i] = getchar();while (getchar() != '\n');        // 清除多余字符}// 初始化邻接矩阵for (i = 0; i < ga->n; i++)for (j = 0; j < ga->n; j++)ga->arcs[i][j] = 0;// 输入边的数量并验证do {printf("请输入边的个数(0-%d):", ga->n * (ga->n - 1) / 2);if (scanf("%d", &e) != 1 || e < 0 || e > ga->n * (ga->n - 1) / 2) {printf("输入错误,请输入有效的边数\n");while (getchar() != '\n');    // 清除输入缓冲区} else {break;}} while (1);while (getchar() != '\n');            // 清除输入缓冲区// 输入边的信息并验证printf("请输入与边相关联的两个顶点的序号(格式:i,j)\n");for (k = 0; k < e; k++) {do {printf("边 %d: ", k + 1);if (scanf("%d,%d", &i, &j) != 2 || i < 0 || i >= ga->n || j < 0 || j >= ga->n) {printf("输入错误,请输入有效的顶点序号(0-%d)\n", ga->n - 1);while (getchar() != '\n'); // 清除输入缓冲区} else {ga->arcs[i][j] = 1;ga->arcs[j][i] = 1;break;}} while (1);while (getchar() != '\n');        // 清除输入缓冲区}return ga;
}// 图的输出
void PrintGraph(GraphMatrix *ga) {if (ga == NULL) {printf("图为空\n");return;}int i, j;printf("\n顶点表为:\n");for (i = 0; i < ga->n; i++)printf("%4d:%c", i, ga->vexs[i]);printf("\n\n邻接矩阵为:\n");printf("   ");for (i = 0; i < ga->n; i++)printf("%4d", i);printf("\n");for (i = 0; i < ga->n; i++) {printf("%2d:", i);for (j = 0; j < ga->n; j++)printf("%4d", ga->arcs[i][j]);printf("\n");}
}// 释放图占用的内存
void DestroyGraph(GraphMatrix *ga) {if (ga != NULL) {free(ga);}
}int main() {GraphMatrix *ga = CreateGraph();if (ga != NULL) {PrintGraph(ga);DestroyGraph(ga); // 释放内存}return 0;
}

运行:

1. 数据结构定义

#define MAXVEX 16                         /*顶点的最大个数*/
typedef char VexType;                     /*顶点的数据类型*/
typedef int AdjType;                      /*邻接矩阵的数据元素的类型*/// 图的邻接矩阵存储结构
typedef struct {VexType vexs[MAXVEX];                 /*顶点表,存储顶点信息*/AdjType arcs[MAXVEX][MAXVEX];         /*邻接矩阵,存储顶点之间的关系*/int n;                                /*图的当前顶点数*/
} GraphMatrix;

这段代码首先定义了顶点的最大个数 MAXVEX ,以及顶点的数据类型 VexType 和邻接矩阵元素的数据类型 AdjType 。然后通过 typedef struct 定义了 GraphMatrix 结构体,它包含了存储顶点信息的数组 vexs 、存储邻接关系的二维数组 arcs ,以及记录当前顶点数的 n 。这个结构体是整个邻接矩阵存储图结构的核心。 

2. 创建图函数 CreateGraph 

GraphMatrix* CreateGraph(void) {int i, j, k, e;GraphMatrix *ga = (GraphMatrix *)malloc(sizeof(GraphMatrix));if (ga == NULL) {fprintf(stderr, "内存分配失败\n");return NULL;}// 输入顶点数量并验证do {printf("请输入顶点的个数(1-%d):", MAXVEX);if (scanf("%d", &(ga->n)) != 1 || ga->n < 1 || ga->n > MAXVEX) {printf("输入错误,请输入1-%d之间的整数\n", MAXVEX);while (getchar() != '\n');    // 清除输入缓冲区ga->n = 0;                    // 重置以继续循环} else {break;}} while (1);// 省略部分代码...return ga;
}

在 CreateGraph 函数中,首先使用 malloc 函数为图结构体分配内存,并检查内存分配是否成功。接着,通过循环和 scanf 函数获取用户输入的顶点个数,并进行严格的输入验证,确保输入的顶点个数在合理范围内。如果输入错误,会提示用户重新输入,并清除输入缓冲区,避免影响后续输入。之后,按照类似的方式获取顶点信息、初始化邻接矩阵、输入边的个数和边的具体信息,每一步都进行了详细的输入验证,保证程序的健壮性。

3. 输出图函数 PrintGraph

void PrintGraph(GraphMatrix *ga) {if (ga == NULL) {printf("图为空\n");return;}int i, j;printf("\n顶点表为:\n");for (i = 0; i < ga->n; i++)printf("%4d:%c", i, ga->vexs[i]);printf("\n\n邻接矩阵为:\n");printf("   ");for (i = 0; i < ga->n; i++)printf("%4d", i);printf("\n");for (i = 0; i < ga->n; i++) {printf("%2d:", i);for (j = 0; j < ga->n; j++)printf("%4d", ga->arcs[i][j]);printf("\n");}
}

PrintGraph 函数用于输出图的顶点表和邻接矩阵。首先检查图指针是否为空,若为空则提示图为空并返回。然后依次输出顶点表,展示每个顶点的序号和对应的字符信息。接着,以整齐美观的格式输出邻接矩阵,添加行列标题,使矩阵更加清晰易懂,方便我们直观地查看图中顶点之间的连接关系。

4. 内存释放函数 DestroyGraph

// 释放图占用的内存
void DestroyGraph(GraphMatrix *ga) {if (ga != NULL) {free(ga);}
}

在程序使用完图结构后,需要释放动态分配的内存,避免内存泄漏。DestroyGraph 函数就是专门用于释放图结构体所占用的内存空间,只要传入的图指针不为空,就调用 free 函数进行释放,确保程序的内存管理合理有效。​

三、邻接矩阵的优缺点与应用场景​

优点​

直观简单:邻接矩阵的存储方式非常直观,通过二维数组可以清晰地看到顶点之间的连接关系,易于理解和实现。​

查询高效:判断两个顶点之间是否有边,只需要访问对应的数组元素,时间复杂度为 ​O(1),对于频繁查询边的操作非常高效。​

方便处理带权图:如果要表示带权图,只需要将邻接矩阵中的元素值改为边的权值即可,实现起来相对简单。​

缺点​

空间浪费:无论图的边数多少,邻接矩阵都需要一个固定大小的二维数组,对于稀疏图(边数远小于顶点数平方的图),会造成大量的空间浪费。​

更新操作复杂:在插入或删除边时,需要修改邻接矩阵中对应的两个元素,并且对于大规模图,这种更新操作的效率较低。​

应用场景​

由于邻接矩阵的特点,它适用于边数较多的稠密图,或者在需要频繁查询边信息、处理带权图的场景中。例如,在一些交通网络的最短路径算法实现中,如果网络连接较为紧密,使用邻接矩阵存储图结构可以方便地进行距离计算和路径查询。​

四、学习数据结构的意义与成长之路​

学习数据结构中的图结构以及邻接矩阵等存储方式,不仅仅是为了完成课程作业和考试,更重要的是培养我们的逻辑思维和问题解决能力。通过对代码的编写、调试和优化,我们能够深入理解数据在计算机中的组织和处理方式,学会如何选择合适的数据结构来解决实际问题。

相关文章:

数据结构中无向图的邻接矩阵详解

在计算机科学的浩瀚宇宙中&#xff0c;数据结构无疑是那把开启高效编程大门的关键钥匙。对于计算机专业的大学生们来说&#xff0c;数据结构课程是专业学习路上的一座重要里程碑&#xff0c;而其中的图结构更是充满魅力与挑战&#xff0c;像一幅神秘的画卷等待我们去展开。今天…...

.NET 7 AOT 使用及 .NET 与 Go 语言互操作详解

.NET 7 AOT 使用及 .NET 与 Go 语言互操作详解 目录 .NET 7 AOT 使用及 .NET 与 Go 语言互操作详解 一、背景与技术概述 1.1 AOT 编译技术简介 1.2 Go 语言与 .NET 的互补性 二、.NET 7 AOT 编译实践 2.1 环境准备 2.2 创建 AOT 项目 2.3 AOT 编译流程 2.4 调试信息处…...

OpenCV 第7课 图像处理之平滑(一)

1. 图像噪声 在采集、处理和传输过程中,数字图像可能会受到不同噪声的干扰,从而导致图像质量降低、图像变得模糊、图像特征被淹没,而图像平滑处理就是通过除去噪声来达到图像增强的目的。常见的图像噪声有椒盐噪声、高斯噪声等。 1.1 椒盐噪声 椒盐噪声(Salt-and-pepper N…...

React 编译器

&#x1f916; 作者简介&#xff1a;水煮白菜王&#xff0c;一位前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 前端专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧和知识归纳总结✍。 感谢支持&#x1f495;&#x1f495;&#…...

HCIP:MPLS静态LSP的配置及抓包

目录 一、MPLS的简单的一些知识点 1.MPLS的概述&#xff1a; 2.MPLS工作原理&#xff1a; 3.MPLS的核心组件&#xff1a; 4. MPLS标签 5.MPLS标签的处理 6.MPLS转发的概述&#xff1a; 7.MPLS的静态LSP建立方式 二、MPLS的静态LSP的实验配置 1.配置接口的地址和配置OS…...

VASP 教程:VASP 结合 Phonopy 计算硅的比热容

VASP 全称为 Vienna Ab initio Simulation Package&#xff08;The VASP Manual - VASP Wiki&#xff09;是一个计算机程序&#xff0c;用于从第一性原理进行原子尺度材料建模&#xff0c;例如电子结构计算和量子力学分子动力学。 Phonopy&#xff08;Welcome to phonopy — Ph…...

YOLO使用SAHI进行小目标检测

目录 一、环境配置二、使用ultralytics的YOLO模型进行训练和推理三、推理可视化的两种方法四、使用SAHI和ultralytics 训练的YOLO模型进行推理一、环境配置 下面是环境的配置过程,根据代码复杂度可以额外安装其他包。 #创建虚拟环境 conda create -n 环境名 python=3.9 #开启…...

[论文阅读]Prompt Injection attack against LLM-integrated Applications

Prompt Injection attack against LLM-integrated Applications [2306.05499] Prompt Injection attack against LLM-integrated Applications 传统提示注入攻击效果差&#xff0c;主要原因在于&#xff1a; 不同的应用对待用户的输入内容不同&#xff0c;有的将其视为问题&a…...

【SpringCache 提供的一套基于注解的缓存抽象机制】

Spring 缓存&#xff08;Spring Cache&#xff09;是 Spring 提供的一套基于注解的缓存抽象机制&#xff0c;常用于提升系统性能、减少重复查询数据库或接口调用。 ✅ 一、基本原理 Spring Cache 通过对方法的返回结果进行缓存&#xff0c;后续相同参数的调用将直接从缓存中读…...

DALI DT6与DALI DT8介绍

“DT”全称Device Type&#xff0c;是DALI-2 标准协议中的IEC 62386-102(即为Part 102)部分对不同类型的控制设备进行一个区分。不同的Device Type代表不同特性的控制设备&#xff0c;也代表了这种控制设备拥有的扩展的特性。 在DALI&#xff08;数字可寻址照明接口&#xff09…...

day13 leetcode-hot100-24(链表3)

234. 回文链表 - 力扣&#xff08;LeetCode&#xff09; 1.转化法 思路 将链表转化为列表进行比较 复习到的知识 arraylist的长度函数&#xff1a;list.size() 具体代码 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode ne…...

Python实战:打造高效通讯录管理系统

&#x1f4cb; 编程基础第一期《8-30》–通讯录管理系统 &#x1f4d1; 项目介绍 在信息化时代&#xff0c;高效管理个人或团队联系人信息变得尤为重要。本文将带您实现一个基于Python的通讯录管理系统&#xff0c;该系统采用字典数据结构和JSON文件存储&#xff0c;实现了联系…...

图解深度学习 - 基于梯度的优化(梯度下降)

在模型优化过程中&#xff0c;我们曾尝试通过手动调整单个标量系数来观察其对损失值的影响。具体来说&#xff0c;当初始系数为0.3时&#xff0c;损失值为0.5。随后&#xff0c;我们尝试增加系数至0.35&#xff0c;发现损失值上升至0.6&#xff1b;相反&#xff0c;当系数减小至…...

MySql--定义表存储引擎、字符集和排序规则

示例&#xff1a; CREATE TABLE users (id INT PRIMARY KEY,name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,email VARCHAR(100) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;注意事项&#xff1a; 字符集和排序规则可以按列覆盖表…...

【部署】在离线服务器的docker容器下升级dify-import程序

回到目录 在离线服务器的docker容器下升级dify-import程序 dify 0.1.0-release 变化很大&#xff0c;重构整个项目代码并且增加制度类txt文件知识库父子分段支持&#xff0c;详见 读取制度类txt文件导入dify的父子分段知识库(20250526发布). 。下面是kylin Linux环境下&#…...

优化版本,增加3D 视觉 查看前面的记录

上图先 运来的超出发表上限&#xff0c;重新发。。。 #11:06:57Current_POS_is: X:77Y:471Z:0U:-2 C:\Log\V55.txt import time import tkinter as tk from tkinter import messagebox from PIL import Image, ImageTk import socket import threading from date…...

写作-- 复合句练习

文章目录 练习 11. 家庭的支持和老师的指导对学生的学术成功有积极影响。2. 缺乏准备和未能适应通常会导致在挑战性情境中的糟糕表现。3. 吃垃圾食品和忽视锻炼可能导致严重的健康问题,因此人们应注重保持均衡的生活方式。4. 昨天的大雨导致街道洪水泛滥,因此居民们迁往高地以…...

WWW22-可解释推荐|用于推荐的神经符号描述性规则学习

论文来源&#xff1a;WWW 2022 论文链接&#xff1a;https://web.archive.org/web/20220504023001id_/https://dl.acm.org/doi/pdf/10.1145/3485447.3512042 最近读到一篇神经符号集成的论文24年底TOIS的&#xff0c;神经符号集成是人工智能领域中&#xff0c;将符号推理与深…...

Linux:shell脚本常用命令

一、设置主机名称 1、查看主机名称 2、用文件的方式更改主机名称 重启后&#xff1a; 3、 通过命令修改主机名 重启后&#xff1a; 二、网络管理命令 1、查看网卡 2、设置网卡 &#xff08;1&#xff09;网卡未被设置过时 &#xff08;2&#xff09;当网卡被设定&#xff0c…...

专业课复习笔记 11

从今天开始每天下午复习专业课。慢慢复习专业课。目标至少考一个一百分吧。毕竟专业课还是比较难的。要是考不到一百分&#xff0c;我感觉自己就废掉了呢。下面稍微复习一下计组。 复习指令格式和数据通路设计。完全看不懂&#xff0c;真是可恶啊。计组感觉就是死记硬背&#…...

OpenTelemetry × Elastic Observability 系列(一):整体架构介绍

本文是 OpenTelemetry Elastic Observability 系列的第一篇&#xff0c;将介绍 OpenTelemetry Demo 的整体架构&#xff0c;以及如何集成 Elastic 来采集和可视化可观测性数据。后续文章将分别针对不同编程语言&#xff0c;深入讲解 OpenTelemetry 的集成实践。 程序架构 Op…...

STM32高级物联网通信之以太网通讯

目录 以太网通讯基础知识 什么是以太网 互联网和以太网的区别 1&#xff09;概念与范围 &#xff08;1&#xff09;互联网 &#xff08;2&#xff09;以太网 2&#xff09;技术特点 &#xff08;1&#xff09;互联网 &#xff08;2&#xff09;以太网 3&#xff09;应…...

从Java的Jvm的角度解释一下为什么String不可变?

从Java的Jvm的角度解释一下为什么String不可变&#xff1f; 从 JVM 的角度看&#xff0c;Java 中 String 的不可变性是由多层次的机制共同保障的&#xff0c;这些设计涉及内存管理、性能优化和安全保障&#xff1a; 1. JVM 内存模型与字符串常量池 字符串常量池&#xff08;St…...

从零开始的数据结构教程(四) ​​图论基础与算法实战​​

&#x1f310; 标题一&#xff1a;图的表示——六度空间理论如何用代码实现&#xff1f; 核心需求 图&#xff08;Graph&#xff09;是用于表达实体间关系的强大数据结构&#xff0c;比如社交网络中的好友关系&#xff0c;或者城市路网的交叉路口连接。关键在于如何高效存储和…...

历年西安交通大学计算机保研上机真题

2025西安交通大学计算机保研上机真题 2024西安交通大学计算机保研上机真题 2023西安交通大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school 计算圆周率近似值 题目描述 根据公式 π / 4 1 − 1 / 3 1 / 5 − 1 / 7 … \pi / 4 1 - 1/3 1/5 - …...

可视化与动画:构建沉浸式Vue应用的进阶实践

在现代Web应用中&#xff0c;高性能可视化和流畅动画已成为提升用户体验的核心要素。本节将深入探索Vue生态中的可视化与动画技术&#xff0c;分享专业级解决方案与最佳实践。 一、 Canvas高性能渲染体系 01、Konva.js流程图引擎深度优化 <template><div class"…...

Python |GIF 解析与构建(3):简单哈希压缩256色算法

Python &#xff5c;GIF 解析与构建&#xff08;3&#xff09;&#xff1a;简单哈希压缩256色算法 目录 Python &#xff5c;GIF 解析与构建&#xff08;3&#xff09;&#xff1a;简单哈希压缩256色算法 一、算法性能表现 二、算法核心原理与实现 &#xff08;一&#xf…...

蓝桥杯2114 李白打酒加强版

问题描述 话说大诗人李白, 一生好饮。幸好他从不开车。 一天, 他提着酒显, 从家里出来, 酒显中有酒 2 斗。他边走边唱: 无事街上走&#xff0c;提显去打酒。 逢店加一倍, 遇花喝一斗。 这一路上, 他一共遇到店 N 次, 遇到花 M 次。已知最后一次遇到的是花, 他正好把酒喝光了。…...

基本数据指针的解读-C++

1、引言 笔者认为对于学习指针要弄清楚如下问题基本可以应付大部分的场景&#xff1a; ① 指针是什么&#xff1f; ② 指针的类型是什么&#xff1f; ③ 指针指向的类型是什么&#xff1f; ④ 指针指向了哪里&#xff1f; 2、如何使用指针 使用时的步骤如下&#xff1a; ① …...

Android Studio里的BLE数据接收策略

#本人是初次接触Android蓝牙开发&#xff0c;若有不对地方&#xff0c;欢迎指出。 #由于是讲接收数据策略(其中还包含数据发送的部分策略)&#xff0c;因此其他问题部分不会讲述&#xff0c;只描述数据接收。 简介(对于客户端---手机端) 博主在处理数据接收的时候&#xff0…...