《信息学C++知识讲解》:图的表示方式

《信息学C++知识讲解》:图的表示方式

图的概念

1、基本术语

图是由节点以及连接这些节点边组成。

2、应用举例

2.1社交网络

在社交网络中所有的用户构成了多对多的朋友关系网,这个关系网就是图:每个人都是图中的节点,互相认识的人之间通过边进行联系。如下图:

在有些图中,节点之间的并不是完全对称的,比如在微信中:A和B互相加了好友,但A单方面删除了B,这样A的好友列表中已经没有了B,但B的好友列表中依然有A,这样图中节点之间的边就有了方向的区分,即为有向图。

在QQ的好友关系中,当A删除了B的时候B的好友列表里也就同步没有了A,即节点之间的边没有方向的区分,即为无向图。

2.2、路线图

在图中,可以给边分配一个数值叫权重,比如在一个路线图中,A市到B市的距离是2km,即从A节点出发到B节点的边的权重是2km,如下图所示:

图的表示方式

1、邻接矩阵

邻接矩阵是表示图中顶点之间相邻关系的矩阵,对于n个顶点的图而言,矩阵的row和col表示的是0-n-1的n个点。

对包含n个节点的图,可以用二维数组(矩阵)来表示节点之间两两的连接关系,如下图:

vector<vector<int>> edges;//用二纬数组表示各点之间两两的连通关系int val = edges[i][j];//表示从节点i到节点j的边,0则表示非连通,有权重时也可以表示权重值

2、邻接列表

邻接矩阵可以方便的得到一个节点到另个节点的关联关系,但是邻接矩阵给每个节点都分配n个边的空间,但实际有许多变是不存在的,会造成空间的浪费。

邻接列表只关心存在的边,不关心不存在的边,如下图所示:

vector<vector<int>> edges(n);//无权图时,用n个一维数组构成的vector来存储邻接列表vector<int> n_edge = edges[i];//存储节点i可以直接访问的节点列表
vector<vector<vector<int>>>edges(n);//无权图时,用n个二维数组构成的vector来存储邻接列表edges[i][j][0];//表示节点i的第j个连通节点编号edges[i][j][1];//表示节点i到第j个连通节点边的权重

3、边缘列表

边缘列表是具有连接顶点及其权重的边的集合,如下图:

vector<vector<int>> edges;//无权图时,用一系列长度为2的vector构成vector来存储边缘列表int u = edges[i][0];//第i条边的起点int v = edges[i][1];//第i条边的终点
vector<vector<int>> edges;//带权图时,用一系列长度为3的vector构成vector来存储边缘列表int u = edges[i][0];//第i条边的起点int v = edges[i][1];//第i条边的终点int val = edges[i][2];//边 u -> v 的权重

图的应用举例

现在你总共有 n 门课需要选,记为 0 到 n-1。在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1] 给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。**说明:** 1. 输入的先决条件是由**边缘列表**表示的图形,而不是邻接矩阵。2. 你可以假定输入的先决条件中没有重复的边。

LeetCode:210.课程表Ⅱ

1、题目分析

要学习一门课程则必须学习完其所有的先决条件课程,即可以用有向图来描述这种依赖关系,节点间的连接关系不对等。

当前可以上的课即为不依赖于任何一门剩下未学习的课的课,即图中入度为0的节点。可以采用BFS进行搜索访问所有节点。

题目中先决条件使用边缘列表来表示图,转换成图的邻接列表后可以方便的获取某个节点可以访问的节点列表、同时记录节点的入度。

2、实现步骤

  • 图的边缘列表转成邻接列表,并记录节点入度
  • 入度为0的节点入队
  • 取出队首u,并将u加入结果数据
  • 将u的所有邻接点的入度减1,更改后入度为0的节点入队
  • 搜索结束后若结果数组的大小等于节点总数则满足要求

3、代码示例

class Solution {public:    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {        vector<vector<int>> edges(numCourses) ;//邻接列表        vector<int> inedg = vector(numCourses,0);//节点的入度        vector<int> res;//结果保存        //由图的边缘列表转换成图的邻接列表,根据u->v,给节点u的邻接点添加v、v的入度加1         for(int i = 0;i < prerequisites.size();i++)        {            edges[prerequisites[i][1]].push_back(prerequisites[i][0]);            inedg[prerequisites[i][0]]++;        }        //BFS搜索,所有入度为0的节点依次入队        queue<int> myqueue;        for(int i = 0;i < numCourses;i++)        {            if(inedg[i] == 0)                myqueue.push(i);        }        while(!myqueue.empty())        {            int u = myqueue.front();myqueue.pop();            res.push_back(u);            //删除该节点后,该节点的所有邻接点的入度依次减1,变更后入度为0的节点入队            for(int i = 0;i < edges[u].size();i++)            {                if( --inedg[edges[u][i]] == 0)                {                    myqueue.push(edges[u][i]);                }            }        }        if( res.size() != numCourses)//返回结果和节点数不相等,则无法完成所有课程            return {};        return res;    }};

公众号

关注公众号

x