有向无环图(邻接矩阵和邻接表)

一、图的定义

图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:

                   G=(V,E)

其中:G表示一个图,V是图G中顶点的集合,E是图G中顶点之间边的集合。

注:

在线性表中,元素个数可以为零,称为空表;

在树中,结点个数可以为零,称为空树;

在图中,顶点个数不能为零,但可以没有边。

 

 

二、图的遍历

   图的遍历是在从图中某一顶点出发,对图中所有顶点访问一次且仅访问一次。

  图的遍历操作要解决的关键问题:

  ① 在图中,如何选取遍历的起始顶点?

  解决方案:从编号小的顶点开始 。

  在线性表中,数据元素在表中的编号就是元素在序列中的位置,因而其编号是唯一的; 在树中,将结点按层序编号,由于树具有层次性,因而其层序编号也是唯一的; 在图中,任何两个顶点之间都可能存在边,顶点是没有确定的先后次序的,所以,顶点的编号不唯一。 为了定义操作的方便,将图中的顶点按任意顺序排列起来,比如,按顶点的存储顺序。

  ② 从某个起点始可能到达不了所有其它顶点,怎么办?

  解决方案:多次调用从某顶点出发遍历图的算法。

  ③ 因图中可能存在回路,某些顶点可能会被重复访问,那么如何避免遍历不会因回路而陷入死循环。

  解决方案:附设访问标志数组visited[n] 。

  ④ 在图中,一个顶点可以和其它多个顶点相连,当这样的顶点访问过后,如何选取下一个要访问的顶点?

  解决方案:深度优先遍历和广度优先遍历。

  1、深度优先遍历

  基本思想 :

  ⑴ 访问顶点v;

  ⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;

  ⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。

  2、广度优先遍历

  基本思想:

  ⑴ 访问顶点v;

  ⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;

  ⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。

三、图的存储结构

    是否可以采用顺序存储结构存储图?

  图的特点:顶点之间的关系是m:n,即任何两个顶点之间都可能存在关系(边),无法通过存储位置表示这种任意的逻辑关系,所以,图无法采用顺序存储结构。

  如何存储图?

  考虑图的定义,图是由顶点和边组成的,分别考虑如何存储顶点、如何存储边。

  ①邻接矩阵(数组表示法)

  基本思想:用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。

  假设图G=(V,E)有n个顶点,则邻接矩阵是一个n×n的方阵,定义为:

 

 

 

以上内容转自https://www.cnblogs.com/smile233/p/8228073.html

上面的内容关于邻接矩阵讲得比较好,对邻接表没有讲解(图都可以通过邻接表和邻接矩阵存储)

邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表。

邻接表的处理方法是这样的:
(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
例如,下图就是一个无向图的邻接表的结构。

这里写图片描述

从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。

从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。

四.两者区别
对于一个具有n个顶点e条边的无向图
它的邻接表表示有n个顶点表结点2e个边表结点
对于一个具有n个顶点e条边的有向图
它的邻接表表示有n个顶点表结点e个边表结点
如果图中边的数目远远小于n2称作稀疏图,这是用邻接表表示比用邻接矩阵表示节省空间;
如果图中边的数目接近于n2,对于无向图接近于n*(n-1)称作稠密图,考虑到邻接表中要附加链域,采用邻接矩阵表示法为宜。

附上leetcode上关于有向无环图的应用的题,课表的安排没有用链表实现利用c++STL中的容器vector去实现。

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
         vector<int> indegree(numCourses,0);//入度表
        vector<vector<int> >lis(numCourses,vector<int>());//用vector来存储邻接表
        for(auto v:prerequisites)
        {
            indegree[v[0]]++;
            lis[v[1]].push_back(v[0]);
        }
        queue<int>q;
        //从入度为0的结点开始处理数据
        for(auto i=0;i<indegree.size();i++)
        {
            if(indegree[i]==0)
            q.push(i);
        }
        vector<int>res;
        while(!q.empty())
        {
            auto temp=q.front();q.pop();
            res.push_back(temp);
            for(auto l:lis[temp])//以temp为头结点的链表
            {
                if(--indegree[l]==0)
                 q.push(l);
            }
        }
       return res.size() == numCourses;
    }
};

 同样的代码修改一下return值就可以解决这个问题

 

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在图论中,有向图(Directed Graph)是一种特殊的数据结构,用于表示一组节点(顶点)之间的有方向的连接。有两种常见的数据结构用于表示有向图:邻接矩阵邻接表。 **1. 邻接矩阵**: 邻接矩阵是一个二维数组,其中的行代表源节点,列表示目标节点。对于有向图中的每条边 (u, v),矩阵中的元素 A[u][v] 将为 1(或非零值),表示从 u 到 v 有一条有向边。如果不存在这条边,则 A[u][v] 通常为 0 或空。邻接矩阵适合于以下情况: - 求解路径查询或判断两点间是否有路径时,时间复杂度为 O(1)。 - 当图的边数相对较多且稠密时,空间效率较高。 **2. 邻接表**: 邻接表则是一个链表数组,每个节点对应一个顶点,链表里存储的是所有指向该节点的边。对于每条边 (u, v),邻接表会有一个链表,其中包含指向从 u 出发到 v 的边的信息。邻接表的主要优点和适用场景是: - 空间效率高,特别适用于稀疏图,即边的数量远小于节点数量的平方。 - 插入和删除边的时间复杂度通常为 O(1),因为只需要更新对应的链表。 - 对于求解路径查询,如果使用深度优先搜索(DFS)或广度优先搜索(BFS),邻接表的表现更好,因为它们不需要预先计算所有可能的路径。 **相关问题--:** 1. 邻接矩阵如何表示无向图? 2. 什么情况下会选择邻接表而不是邻接矩阵? 3. 如何在邻接表中查找某个顶点的所有出边?

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值