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

news2025/6/1 23:42:50

在计算机科学的浩瀚宇宙中,数据结构无疑是那把开启高效编程大门的关键钥匙。对于计算机专业的大学生们来说,数据结构课程是专业学习路上的一座重要里程碑,而其中的图结构更是充满魅力与挑战,像一幅神秘的画卷等待我们去展开。今天,就让我们结合一段精心优化的 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),对于频繁查询边的操作非常高效。​

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

缺点​

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

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

应用场景​

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

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

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

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

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

相关文章

.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…

[论文阅读]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…

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…

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

在模型优化过程中&#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; 字符集和排序规则可以按列覆盖表…

优化版本,增加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…

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…

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;应…

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

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

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

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

基于多模态脑电、音频与视觉信号的情感识别算法【Nature核心期刊,EAV:EEG-音频-视频数据集】

简述 理解情感状态对于开发下一代人机交互界面至关重要。社交互动中的人类行为会引发受感知输入影响的心理生理过程。因此&#xff0c;探索大脑功能与人类行为的努力或将推动具有类人特质人工智能模型的发展。这里原作者推出一个多模态情感数据集&#xff0c;包含42名参与者的3…

【QueryServer】dbeaver使用phoenix连接Hbase(轻客户端方式)

一、轻客户端连接方式 (推荐) 演示无认证配置方式, 有认证填入下方有认证参数即可 1, 新建连接 → Hadoop/大数据 → Apache Phoenix 2, 手动配置QueryServer驱动: 填入: “类名”, “URL模版”(注意区分有无认证), “端口号”, (勾选无认证) 类名: org.apache.phoenix…

[9-1] USART串口协议 江协科技学习笔记(13个知识点)

1 2 3 4全双工就是两个数据线&#xff0c;半双工就是一个数据线 5 6 7 8 9 10 TTL&#xff08;Transistor-Transistor Logic&#xff09;电平是一种数字电路中常用的电平标准&#xff0c;它使用晶体管来表示逻辑状态。TTL电平通常指的是5V逻辑电平&#xff0c;其中&#xff1a;…