力扣刷题-210.课程表Ⅱ、图的表示方式、BFS

一.图的基本概念

  1. 定义和基本术语

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

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第1张图片

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第2张图片

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第3张图片

①.无向图:每条边连接的两个节点可以双向访问。

②.有向图:每条边连接的两个节点只能单向访问。

③.出度:有向图的某个节点作为起点的次数和。

④.入度:有向图的某个节点作为终点的次数和。

⑤.权重:图中每条边分配的值;根据图的边是否有权重,可以分为带权图和无权图。

  1. 应用举例

    ①.社交网络

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

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第4张图片

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

②.路线图

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

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第5张图片

二.图的表示方式

  1. 邻接矩阵

    邻接矩阵是表示图中顶点之间相邻关系的矩阵,对于n个顶点的图而言,矩阵的row和col表示的是0-n-1的n个点。对包含n个节点的图,可以用二维数组(矩阵)来表示节点之间两两的连接关系,如下图:

    力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第6张图片

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

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

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

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第7张图片

vector> edges(n);//无权图时,用n个一维数组构成的vector来存储邻接列表
vector n_edge = edges[i];//存储节点i可以直接访问的节点列表

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第8张图片

vector>>edges(n);//有权图时,用n个二维数组构成的vector来存储邻接列表
edges[i][j][0];//表示节点i的第j个连通节点编号
edges[i][j][1];//表示节点i到第j个连通节点边的权重
  1. 边缘列表

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

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第9张图片

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

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第10张图片

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

三.应用实例

LeetCode:210.课程表Ⅱ

现在你总共有 n 门课需要选,记为 0 到 n-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。

说明:

  1. 输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。
  2. 你可以假定输入的先决条件中没有重复的边。
  1. 题目分析

    ①.题意

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

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第11张图片

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

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

②.步骤

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

    class Solution {
    public:
        vector findOrder(int numCourses, vector>& prerequisites) {
            vector> edges(numCourses) ;//邻接列表
            vector inedg = vector(numCourses,0);//节点的入度
            vector 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 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;
        }
    };
    

力扣刷题-210.课程表Ⅱ、图的表示方式、BFS_第12张图片

你可能感兴趣的:(算法与数据结构,力扣刷题,#,广度优先搜索)