数据结构系列内容的学习目录 → \rightarrow →浙大版数据结构学习系列内容汇总。

  • 1. 什么是图
    • 1.1 图的概述
    • 1.2 抽象数据类型定义
    • 1.3 常见术语
    • 1.4 怎么在程序中表示一个图
      • 1.4.1 邻接矩阵
      • 1.4.2 邻接表
    • 1.5 图的建立
      • 1.5.1 使用邻接矩阵表示图
      • 1.5.2 使用邻接表表示图

1. 什么是图

1.1 图的概述

  (Graph):
    ∙ \bullet ∙ 表示“多对多”的关系
    ∙ \bullet ∙ 包含
     ⋄ \diamond ⋄ 一组顶点:通常用 V(Vertex)表示顶点集合
     ⋄ \diamond ⋄ 一组边:通常用 E(Edge)表示边的集合
      ⋅ \cdot ⋅ 边是顶点对:(v, w) ∈ ∈ ∈ E,其中 v,w ∈ ∈ ∈ V

      ⋅ \cdot ⋅ 有向边 <v,w> 表示从 v 指向 w 的边(单行线)


      ⋅ \cdot ⋅ 不考虑重边和自回路

1.2 抽象数据类型定义

  类型名称: 图(Graph)
  数据对象集: G(V,E)由一个非空的有限顶点集合 V 和一个有限边集合 E 组成
  操作集: 对于任意图 G ∈ ∈ ∈ Graph,以及 v ∈ ∈ ∈ V,e ∈ ∈ ∈ E,主要操作有:

  1. Graph Crate():建立并返回空图;
  2. Graph InsertVertex(Graph G,Vertex v):将v插入G;
  3. Graph InsertEdge(Graph G,Edge e):将e插入G;
  4. void DFS(Graph G,Vertex v):从顶点v出发深度优先遍历图G;
  5. void BFS(Graph G,Vertex v):从顶点v出发宽度优先遍历图G;
  6. void shortestPath (Graph G, vertex v, int Dist[]):计算图G中顶点v到任意其他顶点的最短距离;
  7. void MST (Graph G):计算图G的最小生成树。

1.3 常见术语

  • 无向图:图中所有的边无所谓方向。
  • 有向图: 图中的边可能是双向,也可能是单向的,方向是很重要的。
  • 权值: 给图中每条边赋予的值,可能有各种各样的现实意义。
  • 网络: 带权值的图。
  • 邻接点: 有边直接相连的顶点。
  • 出度: 从某顶点发出的边数。
  • 入度: 指向某顶点的边数。
  • 稀疏图: 顶点很多而边很少的图。
  • 稠密图: 顶点多边也多的图。
  • 完全图: 对于给定的一组顶点,顶点间都存在边。

1.4 怎么在程序中表示一个图

1.4.1 邻接矩阵

  邻接矩阵: G [ N ] [ N ] G[N][N] G[N][N]——N个顶点从0到N-1编号
G [ i ] [ j ] = { 1 若 < v i , v j > 是 G 中 的 边 0 否 则 G[i][j]=\left\{\begin{matrix} 1 \ _{}\ _{}若<v_{i},v_{j}>是G中的边\\ 0 \ _{}\ _{}否则 \ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{}\ _{} \end{matrix}\right. G[i][j]={1 ​ ​若<vi​,vj​>是G中的边0 ​ ​否则 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​​  下图左边所示的图的邻接矩阵如右边所示。


  问题: 对于无向图的存储,怎样可以省一半空间?
  方法: 用一个长度为N(N+1)/2的1维数组A存储 { G 00 , G 10 , G 11 , . . . . . . , G n − 1 0 , . . . . . . , G n − 1 n − 1 } \{G_{00},G_{10},G_{11},......,G_{n-1 \ _{}0} ,......,G_{n-1\ _{} n-1}\} {G00​,G10​,G11​,......,Gn−1 ​0​,......,Gn−1 ​n−1​},则 G i j G_{ij} Gij​在A中对应的下标是 ( i ∗ ( i + 1 ) / 2 + j ) (i*(i+1)/2 +j) (i∗(i+1)/2+j)。


      对于网络,只要把 G [ i ] [ j ] G[i][j] G[i][j]的值定义为边 < v i , v j > <v_{i},v_{j}> <vi​,vj​>的权重即可。
      问题: v i v_{i} vi​和 v j v_{j} vj​之间若没有边该怎么表示?

  优点: 1. 直观、简单、好理解;
      2. 方便检查任意一对顶点间是否存在边;
      3. 方便找任一顶点的所有“邻接点”(有边直接相连的顶点)
      4. 方便计算任一顶点的“度”(从该点发出的边数为“出度”,指向该点的边数为“入度”)
        ● 无向图:对应行(或列)非0元素的个数
        ● 有向图:对应行非0元素的个数是“出度”;对应列非0元素的个数是“入度”。

  缺点: 1. 浪费空间:存稀疏图(点很多而边很少)有大量无效元素;
        ● 对稠密图(特别是完全图)还是很合算的
      2. 浪费时间:统计稀疏图中一共有多少条边。

1.4.2 邻接表

  邻接表: G[N]为指针数组,对应矩阵每行一个链表,只存非0元素。

  下图左边所示的图的邻接表如右边所示。


  对于网络,结构中要增加权重的域。

  特点: 1. 方便找任一顶点的所有“邻接点”;
      2. 节约稀疏图的空间;
        ● 需要N个头指针+2E个结点(每个结点至少2个域)
      3. 方便计算任一顶点的“度”?
        ● 对无向图:是的
        ● 对有向图:只能计算“出度”,需要构造“逆邻接表”(存指向自己的边)来方便计算“入度”
      4. 方便检查任意一对顶点间是否存在边? No

1.5 图的建立

  对于图1所示的图,分别用邻接矩阵和邻接表表示法建立图。

图1

1.5.1 使用邻接矩阵表示图

  使用邻接矩阵表示法建立图的代码实现如下所示。

#include<iostream>
using namespace std;/* 图的邻接矩阵表示法 */
#define MaxVertexNum 100    // 最大顶点数设为100
typedef int Vertex;         // 用顶点下标表示顶点,为整型
typedef int WeightType;        // 边的权值设为整型
typedef char DataType;        // 顶点存储的数据类型设为字符型/* 边的定义 */
typedef struct ENode *PtrToENode;
struct ENode {Vertex V1, V2;    // 有向边<V1,V2> WeightType Weight;  // 权重
};
typedef PtrToENode Edge;/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode {int Nv;   // 顶点数 int Ne;   // 边数WeightType G[MaxVertexNum][MaxVertexNum];  //邻接矩阵DataType Data[MaxVertexNum]; // 存顶点的数据 /* 注意:很多情况下,顶点无数据,此时Data[]可以不用出现 */
};
typedef PtrToGNode MGraph;  // 以邻接矩阵存储的图类型// 初始化图
MGraph CreateGraph(int VertexNum)
{Vertex V, W;MGraph Graph;Graph = (MGraph)malloc(sizeof(struct GNode));  // 建立图Graph->Nv = VertexNum;Graph->Ne = 0;/* 初始化邻接矩阵 *//* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */for (V = 0; V < VertexNum; V++)for (W = 0; W < VertexNum; W++)Graph->G[V][W] = 0;return Graph;
}// 插入边
void InsertEdge(MGraph Graph, Edge E)
{// 插入边 <V1,V2>Graph->G[E->V1][E->V2] = E->Weight;// 如果是无向图,还需要插入边 <V2,V1>Graph->G[E->V2][E->V1] = E->Weight;
}// 建图
MGraph BuildGraph()
{MGraph Graph;Edge E;Vertex V;int Nv, i;cin >> Nv;   // 读入顶点数 Graph = CreateGraph(Nv);  // 初始化有Nv个顶点但没有边的图cin >>(Graph->Ne);  // 读入边数 if (Graph->Ne != 0)  // 如果有边{E = (Edge)malloc(sizeof(struct ENode));  // 建立边结点for (i = 0; i < Graph->Ne; i++) {cin >> E->V1 >> E->V2 >> E->Weight;// 读入边,格式为"起点 终点 权重",插入邻接矩阵 InsertEdge(Graph, E);}}// 如果顶点有数据的话,读入数据for (V = 0; V < Graph->Nv; V++)cin >> (Graph->Data[V]);return Graph;
}// 遍历图
void Print(MGraph Graph)
{Vertex V, W;for (V = 0; V < Graph->Nv; V++) {for (W = 0; W < Graph->Nv; W++)cout << Graph->G[V][W];cout << endl;}
}int main()
{MGraph Graph;Graph = BuildGraph();Print(Graph);system("pause");return 0;
}

  对于图1所示的图,使用邻接矩阵表示法建立图的测试效果如下图所示。

1.5.2 使用邻接表表示图

  使用邻接表表示法建立图的代码实现如下所示。

#include<iostream>
using namespace std;/* 图的邻接表表示法 */
#define MaxVertexNum 100  // 最大顶点数设为100
typedef int Vertex;  // 用顶点下标表示顶点,为整型
typedef int WeightType;  // 边的权值设为整型
typedef char DataType;  // 顶点存储的数据类型设为字符型/* 边的定义 */
typedef struct ENode *PtrToENode;
struct ENode {Vertex V1, V2;  // 有向边<V1, V2>WeightType Weight;  // 权重
};
typedef PtrToENode Edge;/* 邻接点的定义 */
typedef struct AdjVNode *PtrToAdjVNode;
struct AdjVNode {Vertex AdjV;  // 邻接点下标WeightType Weight;  // 边权重PtrToAdjVNode Next;  // 指向下一个邻接点的指针
};/* 顶点表头结点的定义 */
typedef struct Vnode {PtrToAdjVNode FirstEdge;  // 边表头指针DataType Data;  // 存顶点的数据/* 注意:很多情况下,顶点无数据,此时Data可以不用出现 */
} AdjList[MaxVertexNum];  // AdjList是邻接表类型/* 图结点的定义 */
typedef struct GNode *PtrToGNode;
struct GNode {int Nv;  // 顶点数int Ne;  // 边数AdjList G;  // 邻接表
};
typedef PtrToGNode LGraph;  // 以邻接表方式存储的图类型LGraph CreateGraph(int VertexNum)  // 初始化一个有VertexNum个顶点但没有边的图
{Vertex V;LGraph Graph;Graph = (LGraph)malloc(sizeof(struct GNode));  // 建立图Graph->Nv = VertexNum;  // 初始化边Graph->Ne = 0;  // 初始化点/* 初始化邻接表头指针 *//* 注意:这里默认顶点编号从0开始,到(Graph->Nv - 1) */for (V = 0; V < Graph->Nv; V++)Graph->G[V].FirstEdge = NULL;return Graph;
}void InsertEdge(LGraph Graph, Edge E)  // 插入一条边到邻接表的顶点指针之后
{PtrToAdjVNode NewNode;/* 插入边 <V1, V2> */// 为V2建立新的邻接点NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));NewNode->AdjV = E->V2;NewNode->Weight = E->Weight;// 将V2插入V1的表头NewNode->Next = Graph->G[E->V1].FirstEdge;Graph->G[E->V1].FirstEdge = NewNode;/* 若是无向图,还要插入边 <V2, V1> */// 为V1建立新的邻接点NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));NewNode->AdjV = E->V1;NewNode->Weight = E->Weight;// 将V1插入V2的表头NewNode->Next = Graph->G[E->V2].FirstEdge;Graph->G[E->V2].FirstEdge = NewNode;
}// 建图
LGraph BuildGraph()
{LGraph Graph;Edge E;Vertex V;int Nv, i;cin >> Nv;  // 读入顶点个数Graph = CreateGraph(Nv);  // 初始化有Nv个顶点但没有边的图cin >> (Graph->Ne);  // 读入边数if (Graph->Ne != 0)  // 如果有边{E = (Edge)malloc(sizeof(struct ENode));  // 建立边结点for (i = 0; i < Graph->Ne; i++) {cin >> E->V1 >> E->V2 >> E->Weight;  // 读入边,格式为"起点 终点 权重",插入邻接矩阵InsertEdge(Graph, E);}}// 如果顶点有数据的话,读入数据for (V = 0; V < Graph->Nv; V++)cin >> (Graph->G[V].Data);return Graph;
}// 打印
void Print(LGraph Graph)
{Vertex v;PtrToAdjVNode temp;for (v = 0; v < Graph->Nv; v++) {temp = Graph->G[v].FirstEdge;cout << v << " ";while (temp) {cout << temp->AdjV << " ";temp = temp->Next;}cout << endl;}
}int main()
{LGraph Graph;Graph = BuildGraph();Print(Graph);system("pause");return 0;
}

  对于图1所示的图,使用邻接表表示法建立图的测试效果如下图所示。

数据结构(四)—— 图(1):什么是图相关推荐

  1. 高阶数据结构(1):并查集 与 图

    "Head in the clouds" 一.并查集 (1)认识并查集? 在一些问题中需要将n个不同的元素划分成 一些不想交的集合. 开始时,每个元素自成一个单元素集合,然后按一定 ...

  2. 数据结构与算法(C++)– 图(Graph)

    数据结构与算法(C++)– 图(Graph) 1.图的基础概念 定义:一个图G=(V, E)由顶点(vertex)的集V和边(edge)的集E组成. 边(edge):一对点即为一条边(v, w),其中 ...

  3. spi四种工作模式时序图_SPI总线协议及SPI时序图详解

    嵌入式linux QQ交流群:175159209,欢迎爱好者加入交流技术问题! SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种 ...

  4. 野生前端的数据结构基础练习(8)——图

    [摘要] 图是由边的集合和点的集合组成的.如果图的边有方向(或者说图中的顶点对是有序的)则成为有向图,如果边没有方向则称为无向图. 网上的相关教程非常多,基础知识自行搜索即可. 习题主要选自Orell ...

  5. 分别用邻接矩阵和邻接表实现图的深度优先遍历和广度优先遍历_数据结构与算法:三十张图弄懂「图的两种遍历方式」...

    原创: 进击的HelloWorld1 引言遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次. 在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点 ...

  6. c++层次遍历_数据结构与算法,弄懂图的两种遍历方式

    1 引言   遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次.  在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点出发,按照一定的访问规则, ...

  7. 深度优先遍历访问的边集合_数据结构与算法: 三十张图弄懂「图的两种遍历方式」...

    1 引言 遍历是指从某个节点出发,按照一定的的搜索路线,依次访问对数据结构中的全部节点,且每个节点仅访问一次. 在二叉树基础中,介绍了对于树的遍历.树的遍历是指从根节点出发,按照一定的访问规则,依次访 ...

  8. 数据结构笔记(二十七)-- 图的深度优先遍历

    图的深度优先遍历 一.图遍历定义 图的遍历是指从图的某个顶点出发遍历图,访遍图中其余顶点,并且使图中的每个顶点仅被访问一次的过程 二.深度优先遍历的思想 三.深度优先遍历举例 四.伪代码

  9. Carson带你学数据结构:手把手带你了解 ”图“ 所有知识!(含DFS、BFS)

    前言 本文主要讲解 数据结构中的图 结构,包括 深度优先搜索(DFS).广度优先搜索(BFS).最小生成树算法等,希望你们会喜欢. 目录 1. 简介 数据结构的中属于 圆形结构 的逻辑结构 具体介绍如 ...

  10. 猿人学web端爬虫攻防大赛赛题解析_第四题:雪碧图、样式干扰

    第四题:雪碧图 - 样式干扰 1.前言 2.题目理解 3.解析过程 3.1.初窥门径 3.2.深入探究 3.2.1.确定原理 3.2.2.逆向破解 3.2.代码实现 4.结语 1.前言 久违的第四题终 ...

最新文章

  1. 中国水泥电杆市场全景调研及发展战略研究报告2022-2028年版
  2. 不写画面的网页程序设计,Web API、Web Service、WCF Service
  3. JavaScript——分时问候
  4. 《Hadoop MapReduce实战手册》一1.4 给WordCount MapReduce程序增加combiner步骤
  5. struts+hibernate+oracle+easyui实现lazyout组件的简单案例——Action的实现类
  6. 大佬对大佬,史诗级面试现场!!!
  7. vmstat工具详解
  8. 基于嵌入式操作系统VxWorks的多任务并发程序设计(4)――任务间通信A
  9. python字典值求和_Python两个字典键同值相加的几种方法
  10. 提示内存不足,但内存明明很多
  11. ODBC连接MySQL
  12. python计算sinx在0-2π_定积分[0,2π]|sinx|
  13. 关于笔记本电脑网卡出问题的简单解决
  14. stm32+ESP8266实现最简单的手机控制LED灯
  15. 实现sqlite datediff日期时间相减(日期差)的方法
  16. WIN10 本地打印机没有“打印机属性”
  17. Quixel bridge无法导入到blender
  18. 亿图图示----组织架构图----市场组织架构和公司架构图及家庭架构图
  19. App Store上推广App的实战经验
  20. keras学习之:获取神经网络中间层的输出结果

热门文章

  1. react-navigation 6.x 学习(3)
  2. 录用了一篇 Omega
  3. 最热门的十种编程语言,总有一种适合你
  4. check 和nocheck
  5. 2021年会议平板十大品牌榜中榜,十大会议平板品牌销量排行榜
  6. 怎么将word转换成excel表格格式最简单
  7. 径向基函数神经网络_神经网络
  8. 瞻博网络持续深耕 以“本土化”撬动“中国心”
  9. 加油------加油
  10. leetcode 179.最大数