基础数据结构 -- 图

news2024/9/25 13:24:46

1 简介

        图是一种用 节点 和 边 来表示相互关系的数学模型,其中节点代表事物,边代表事物间的联系。图作为一种数据结构,在各种领域都有广泛应用,如社交网络、城市交通网络、互联网页面链接等。

图由两个主要部分构成:

        节点(也称为顶点)和边。

        节点代表图中的基本单元,可以是具有某种特性的实体,例如人、地点或其他对象。边则代表这些节点之间的关系或连接。

根据边的方向性,可以分为

        有向图和无向图。

        无向图中的边没有方向,通常用来表示双向对称的关系;而有向图中的边有明确的方向,用以表示非对称的关系。

2 图解

2.1 分类

2.1.1 无向图

2.1.2 有向图

2.2 结构

2.2.1 邻接矩阵

  • 无向图:在无向图中,邻接矩阵是对称的,矩阵中的每个元素[i][j]表示顶点i与顶点j之间是否存在边。如果是1,则存在边;如果是0,则不存在边。
  • 有向图:在有向图中,有向图的邻接矩阵不一定对称。矩阵中的元素[i][j]表示从顶点i到顶点j是否有一个箭头。这实现了有向图的方向性特性。
  • 带权图:当图的边被赋予权值时,这些权值可以代替邻接矩阵中的1,表示顶点间的距离或成本。如果两个顶点之间没有直接的边,则通常用一个很大的数来表示不可达
  • 特点:邻接矩阵可以快速确定两个顶点之间是否存在边。然而,对于稀疏图来说,邻接矩阵可能浪费大量的存储空间,因为它存储了许多不存在的边的信息。
public class AdjacencyMatrix {
    private int[][] matrix; // 邻接矩阵
    private int numVertices; // 顶点数量

    // 构造函数,初始化邻接矩阵和顶点数量
    public AdjacencyMatrix(int numVertices) {
        this.numVertices = numVertices;
        matrix = new int[numVertices][numVertices];
    }

    // 添加一条边
    public void addEdge(int i, int j) {
        if (i >= 0 && i < numVertices && j >= 0 && j < numVertices) {
            matrix[i][j] = 1; // 有向图
            matrix[j][i] = 1; // 无向图
        }
    }

    // 删除一条边
    public void removeEdge(int i, int j) {
        if (i >= 0 && i < numVertices && j >= 0 && j < numVertices) {
            matrix[i][j] = 0; // 有向图
            matrix[j][i] = 0; // 无向图
        }
    }

    // 检查两个顶点之间是否存在边
    public boolean hasEdge(int i, int j) {
        if (i >= 0 && i < numVertices && j >= 0 && j < numVertices) {
            return matrix[i][j] == 1;
        }
        return false;
    }

    // 打印邻接矩阵
    public void printMatrix() {
        for (int i = 0; i < numVertices; i++) {
            for (int j = 0; j < numVertices; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        AdjacencyMatrix graph = new AdjacencyMatrix(6);
        graph.addEdge(6, 5);
        graph.addEdge(5, 2);
        graph.addEdge(2, 1);
        graph.addEdge(5, 4);
        graph.addEdge(4, 3);
        graph.addEdge(2, 3);
        graph.addEdge(1, 3);

        graph.printMatrix();
    }
}

2.2.2 邻接表

  • 无向图:在无向图的邻接表中,每个顶点都有一个与之关联的链表,记录了所有与其相邻的顶点。这种方法只存储存在的边,对稀疏图来说很有效。
  • 有向图:对于有向图,邻接表记录每个顶点发出的边。有时为了方便获取顶点的入度,会额外建立一个逆邻接表来快速查找以某个顶点为终点的边。
  • 带权图:带权值的网在邻接表中存储时,每条边除了要记录邻接顶点外,还要记录边的权值。这使得计算路径权重更加直接。
  • 特点:邻接表适合表示稀疏图节约存储空间。但是,相比于邻接矩阵,邻接表在判断两个顶点间是否存在边时效率较低。
public class AdjacencyList {
    private List<List<Integer>> adjacencyList; // 邻接表
    private int numVertices; // 顶点数量

    // 构造函数,初始化邻接表和顶点数量
    public AdjacencyList(int numVertices) {
        this.numVertices = numVertices;
        adjacencyList = new ArrayList<>(numVertices);
        for (int i = 0; i < numVertices; i++) {
            adjacencyList.add(new ArrayList<>());
        }
    }

    // 添加一条边
    public void addEdge(int i, int j) {
        if (i >= 0 && i < numVertices && j >= 0 && j < numVertices) {
            adjacencyList.get(i).add(j); // 有向图
            adjacencyList.get(j).add(i); // 无向图
        }
    }

    // 删除一条边
    public void removeEdge(int i, int j) {
        if (i >= 0 && i < numVertices && j >= 0 && j < numVertices) {
            adjacencyList.get(i).remove((Integer) j); // 有向图
            adjacencyList.get(j).remove((Integer) i); // 无向图
        }
    }

    // 检查两个顶点之间是否存在边
    public boolean hasEdge(int i, int j) {
        if (i >= 0 && i < numVertices && j >= 0 && j < numVertices) {
            return adjacencyList.get(i).contains(j);
        }
        return false;
    }

    // 打印邻接表
    public void printAdjacencyList() {
        for (int i = 0; i < numVertices; i++) {
            System.out.print("Vertex " + i + ": ");
            for (int j : adjacencyList.get(i)) {
                System.out.print(j + " ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        AdjacencyList graph = new AdjacencyList(6);
        graph.addEdge(6, 5);
        graph.addEdge(5, 2);
        graph.addEdge(2, 1);
        graph.addEdge(5, 4);
        graph.addEdge(4, 3);
        graph.addEdge(2, 3);
        graph.addEdge(1, 3);

        graph.printAdjacencyList();
    }
}

2.2.3 其他

还有一些用于特定的场景和需求,比如十字链表、邻接多重表和边集数组则等,知道即可,就不多赘述。

  1. 十字链表

    十字链表结合了邻接表和逆邻接表的优点,使得在有向图中可以同时快速获取一个顶点的入度和出度信息。此外,它在一定程度上保持了邻接表的存储效率。
  2. 邻接多重表

    邻接多重表对无向图的存储结构进行了优化,使得边的操作更加方便。与邻接表相比,每条边在邻接多重表中只由一个结点表示,而不是两个。
  3. 边集数组

    边集数组强调的是边的集合,它使用两个一维数组,分别存储图中每个顶点的信息和边的信息。这种方法在需要对边进行依次处理时比较适用,但查找一个顶点的邻居需要扫描整个边数组,效率并不高。

4 总结

本章主要介绍了图的两种结构。在此对以上总结:

邻接矩阵

  • 表示方法:使用一个二维数组,其中行和列代表图中的顶点,矩阵中的元素表示顶点之间的边。
  • 适用场景:适合于稠密图,即图中的边数量接近于顶点数量的平方。
  • 优点:查询顶点之间是否存在边的时间复杂度为O(1)。
  • 缺点:空间复杂度高,为O(V^2),其中V是顶点数量;更新图(添加或删除顶点和边)的成本较高。

邻接表

  • 表示方法:使用一个列表数组,每个顶点对应一个列表,列表中包含所有与该顶点相邻的顶点。
  • 适用场景:适合于稀疏图,即图中的边数量远少于顶点数量的平方。
  • 优点:空间复杂度低,为O(V+E),其中V是顶点数量,E是边数量;更新图的操作较为高效。
  • 缺点:查询顶点之间是否存在边的时间复杂度为O(degree(v)),其中degree(v)是顶点v的度。

在选择图的存储结构时,需要考虑以下因素:

  • 图的类型:有向图、无向图、加权图等。
  • 图的密度:稀疏图还是稠密图。
  • 操作类型:频繁执行的操作,如查找、插入、删除等。
  • 空间和时间效率:根据应用场景选择合适的存储结构以优化性能。

总之,邻接矩阵和邻接表各有优劣,适用于不同的场景。在实际应用中,应根据具体需求和图的特性来选择最合适的存储结构。

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

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

相关文章

XXX【4】策略模式

如上图所示&#xff0c;如果要加入一个新的货币&#xff0c;那么就需要对类中的Calculate函数进行修改&#xff0c;这违背了封闭开放原则。 上图中的方式更加合适&#xff0c;搞一个抽象类&#xff08;方法中可以用多态调用&#xff09;&#xff0c;然后每个货币自己是一个类&a…

井盖异动传感器:为城市安全加码

城市的地下管网错综复杂&#xff0c;井盖作为连接地面与地下的重要通道&#xff0c;其安全性至关重要。然而&#xff0c;由于各种原因导致的井盖丢失或损坏事件时有发生&#xff0c;给行人和车辆带来了极大的安全隐患。 一、智能科技&#xff0c;守护脚下安全 旭华智能井盖异…

3D可视化项目

3D可视化项目是指利用3D技术和工具来将数据、场景或概念以可视化的方式呈现出来&#xff0c;以便更好地理解和分析。这些项目通常涉及使用3D建模、渲染和动画技术&#xff0c;将数据或概念转化为具有深度和逼真感的可视化场景或对象。3D可视化项目涉及到的技术栈主要包括以下几…

从零开始掌握限流技术:计数器、滑动窗口、漏桶与令牌桶详解

为什么需要限流呢&#xff1f; &#x1f539;想象一下&#xff0c;你的服务器就像一个繁忙的餐馆&#xff0c;而你的应用就像是餐馆的服务员。餐馆里人山人海&#xff0c;每个人都在争先恐后地想要点餐。这时候&#xff0c;如果没有一个好的限流机制&#xff0c;会发生什么呢&…

【轻松拿捏】设计模式六大基本原则(一)单一职责原则(SRP - Single Responsibility Principle)

&#x1f388;边走、边悟&#x1f388;迟早会好 一. 概述 单一职责原则&#xff08;SRP - Single Responsibility Principle&#xff09;是面向对象设计中的一个基本原则。它的核心思想是&#xff1a;一个类只应有一个引起它变化的原因&#xff0c;也就是说&#xff0c;一个类…

使用Docker-compose一键部署Wordpress平台

一、Docker-compose概述&#xff1a; docker-compose&#xff1a;单机容器编排 Dockerfile&#xff1a;先配置好文件&#xff0c;然后build&#xff0c;镜像——>容器。 docker-compose&#xff1a;即可基于Dockerfile&#xff0c;也可以基于镜像&#xff0c;可以一键式拉…

扎心“我学了六个月 Python,怎么还是会找不到工作”

前言 &#x1f449; 小编已经为大家准备好了完整的代码和完整的Python学习资料&#xff0c;朋友们如果需要可以扫描下方CSDN官方认证二维码或者点击链接免费领取【保证100%免费】 在编程界&#xff0c;Python是一种神奇的存在。有人认为&#xff0c;只有用Python才能优雅写代码…

qt-16可扩展对话框--隐藏和展现

可扩展对话框 知识点extension.hextension.cppmain.cpp运行图初始化隐藏展现--点击--详细按钮 知识点 MainLayout->setSizeConstraint(QLayout::SetFixedSize);//固定窗口大小 extension.h #ifndef EXTENSION_H #define EXTENSION_H#include <QDialog>class Extens…

如何对 GitLab 中文版进行升级?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门面向中国程序员和企业提供企业级一体化 DevOps 平台&#xff0c;用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规&#xff0c;而且所有的操作都是在一个平台上进行&#xff0c;省事省心省钱。可以一键安装极狐GitL…

EVE-NG导入锐捷镜像

安装搭建好了EVE-NG之后&#xff0c;我们想要做虚拟实验还需要载入虚拟镜像&#xff0c;下面我来演示如何导入锐捷镜像 准备工作&#xff1a; 1.安装EVE-NG 2.FTP远程传输工具 正式开始&#xff1a; 第一步&#xff1a;下载镜像 前往锐捷官网下载锐捷设备镜像 锐捷镜像下…

以太网链路聚合(LACP模式)

1. 实验目的 掌握使用静态LACP模式配置链路聚合的方法掌握控制静态LACP模式下控制活动链路的方法掌握静态LACP的部分特性的配置 实验拓扑 实验拓扑如图11-12所示&#xff1a; 图11-12以太网聚合LACP模式 3. 实验步骤 PC机IP地址的配置 PC1的配置&#xff0c;在ipv4下选…

内存(动态开辟)———C语言

内存管理&#xff1a; 1.C语言运行时的内存分配 2.static关键字 1.修饰变量 局部变量&#xff1a; <1>在编译的过程中&#xff0c;会在数据区为该变量开辟空间&#xff0c;如果代码中未对其进行初始化&#xff0c;则系统默认初始化为0。 <2>用static修饰的局部变量…

a股市场的股票怎么做期权交易

在金融市场中&#xff0c;A股股票期权是一种重要的衍生金融工具&#xff0c;它赋予持有者在未来某一特定日期以预定价格买入或卖出股票的权利。了解期权交易流程对于投资者至关重要&#xff0c;本文将详细介绍a股市场的股票怎么做期权交易&#xff1f;本文来自&#xff1a;期权…

【软件测试】测试阶段、评审、瀑布式流程

软件测试 测试阶段评审瀑布式流程 测试阶段 冒烟测试&#xff08;Smoke Testing&#xff09;&#xff1a; 冒烟测试是一种快速的初步测试&#xff0c;通常在开发周期的早期进行&#xff0c;用以验证软件的基本功能是否按预期工作。它通常在软件构建完成后立即执行&#xff0c;以…

【windows系统】应用与功能下卸载程序提示Windows找不到文件解决办法

我们使用windows系统的时候&#xff0c;想要删除某个程序&#xff0c;可以通过windows系统设置中的添加或删除程序来操作&#xff0c;如下图所示 打开如图下所示应用和功能界面&#xff0c;可以对程序进行卸载 如图下所示&#xff0c;我们可以通过这种方式卸载掉电脑中的程序 但…

ClickHouse集群的安装

目录 1.clickhouse中文文档地址 2.centos安装部署 2.1采用tgz的方式安装 2.2修改配置文件 2.3修改数据目录 2.4创建角色和目录 3 集群安装 3.1配置文件修改 3.2启动zookeeper 3.3启动clickhouse-server 3.4任意节点连接clickhouse 3.5查看集群 3.6建库 3.7查看数…

【Linux 驱动】IMX6ULL pinctrl驱动

1. 概述 Linux 驱动讲究驱动分离与分层&#xff0c;pinctrl 和 gpio 子系统就是驱动分离与分层思想下的产物。pinctrl顾名思义就是引脚控制&#xff0c;用来配置比如引脚mux复用信息&#xff0c;引脚电器属性&#xff08;比如上/下拉、速度、驱动能力等&#xff09;信息。gpio顾…

学会区分大模型——大模型的分类,让你更清晰的认识大模型

乱花渐欲迷人眼&#xff0c;学会从根本上认识问题 现在市面上大模型如百花齐放&#xff0c;对很多人来说一堆大模型带来的不是简单方便&#xff0c;而是乱七八糟以及迷茫。 因为不知道不同的大模型之间有什么区别&#xff0c;也不知道自己需要什么样的大模型&#xff1b;就拿…

【C++题解】1375. 拦截导弹方案求解

问题&#xff1a;1375. 拦截导弹方案求解 类型&#xff1a;贪心 题目描述&#xff1a; 某国为了防御敌国的导弹袭击&#xff0c;发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷&#xff1a; 虽然它的第一发炮弹能够到达任意的高度&#xff0c;但是以后每一发炮弹都…

理解Flink数据流图

前言 在大数据处理的世界里&#xff0c;Apache Flink 已经成为处理实时数据流的一个强大工具。Flink 提供了一种高度灵活的方法来构建复杂的数据处理管道&#xff0c;其核心是所谓的 DataFlow 图模型。本文将带你深入了解 Flink DataFlow 图的基础知识&#xff0c;帮助你理解它…