图的两种表示方法

 

图的两种表示方法

 

前面我们简单的介绍了一些关于图的基础知识,这一次我们来讨论一下在计算机中如何去描述一个图,采用怎样的一个数据结构。前面我们看到,一个图的基本组成就是节点和边,因此,我们只想找到一种描述节点并且节点之间边关系的数据结构就好了。通常我们使用两种不同的表示方法来表示一个图:

 

1.邻接矩阵法

2.邻接表法

这两种表示方法对于图中的点来说都是一样的,区别在与对点与点之间的边表示存在着不同。

 

 

①邻接矩阵表示法

 

《图的两种表示方法》

 

如上图所示,图中有4个节点,则对应的邻接表中就有4行4列,如果这个矩阵命名为a的话,则a[i][j]的值代表着i节点与j节点之间是否存在着边,我们用布尔值0和1来表示两种状态,0表示不存在边,即两点之间无连接。1表示有连接,即两点之间存在着边连接。我们现在讨论的是无向图,则a[i][j]与a[j][i]表示的值是一样的,因为假如1节点与2节点之间存在边关系,则2节点与1节点肯定也是存在着边关系的。因此,无向图的邻接矩阵中关于斜对角线对称。如果用邻接矩阵来表示有向图的话,则不一定对称,因此时的a[i][j]表示的是存在i节点指向j节点的边,而j节点是否有指向i节点的边就不一定了。

 

 

②邻接表表示法

 

 

 

《图的两种表示方法》

如上图所示,邻接表与邻接矩阵的不同之处就在于:邻接矩阵把所有点与点之间的关系是否存在都表示出来了,而邻接矩阵只把存在关系的点表示出来,没有表示则表明不存在着边关系。

例如上图中,第0行只有1个1节点,即表示与0节点相连的节点只有1节点,第1行有0,2,3这3个节点,表示着和1节点相连的节点有3个,即0节点,2节点,3节点。第2行后面有1,3节点,表示与2节点相连的节点有两个,分别是1节点和3节点,以此类推……

当然,邻接表表示法也可以用来表示有向图,如下图所示:

《图的两种表示方法》

 

则表示0节点有指向1节点的一条边,1节点有一条指向2节点的边,2节点有指向3节点的一条边,3节点有指向1节点的一条边,即此时表示的边关系都是带有方向的。

 

 

在上面我们可以看出,邻接表相比于邻接矩阵来说,所占用的空间更小,这是邻接表的一个优势。但是邻接表如果表示的是一个有很多条边的图,即稠密图的话,则邻接表的优势就不能够完好的体现了。因此,对于一个图来说,我们要根据具体的情况来判断使用哪种方式去表示该图,一般邻接表适合表示稀疏图,邻接矩阵适合表示稠密图。

 

 

图的具体实现

 

下面来具体的实现一下两种图的表示方法:

 

稠密图类的实现(使用邻接矩阵表示):

class DenseGraph{//构建稠密图类,使用邻接矩阵法表示
private:
    int n,m;//n为图的顶点数量,m为边的数量
    bool directed;//是否为有向图
    vector<vector<bool>> g;//构建二维数组g作为邻接表的结构基础,其存储类型为布尔类型
public:
    DenseGraph(int n,bool directed ){//构造函数
        this->n=n;
        this->directed=directed;
        this->m=0;
        for(int i=0;i<n;i++){
            g.push_back(vector<bool>(n, false));//初始化邻接表的关系全部为false,即各节点之
//间都不连接
        }
         }

    ~DenseGraph(){

    }

    int V(){//返回图中的顶点数量
        return n;
    };
    int E(){//返回图中的边数量
            return m;
    };

    void addEdge(int v,int w){//在v节点和w节点之间建立连接关系
        assert(v>=0&&v<n);
        assert(w>=0&&w<n);//防止数组越界访问
        if(hasEdge(v,w)) return;//v节点存在到w节点的边,则不需要进行加边操作,直接返回即可
        g[v][w]= true;
        m++;//增加边的数量
        if(directed){//如果为有向图
           return;
        }
        else{//如果为无向图,则邻接图成对称关系,w节点到v节点之间也一定是存在连接的
            g[w][v]= true;
        }
    }

    bool hasEdge(int v,int w){//判断v节点到w节点之间是否已经存在边
        assert(v>=0&&v<n);//防止越界
        assert(w>=0&&w<n);
        return g[v][w];
    }

};

 

 

稀疏图类的实现(使用邻接表表示)

 

class SparseGraph{//构建稀疏图,并使用邻接表结构
private:
    int n,m;//n为图的节点数量,m为图的边数量
    bool directed;//表示图是否为有向图
    vector<vector<int>> g;//使用二维数组来表示邻接表,且数据类型为int类型
public:
    SparseGraph(int n, bool directed){//稀疏图的构造函数
        this->n=n;
        this->m=0;
        this->directed=directed;
        for(int i=0;i<n;i++){
            g.push_back(vector<int>());
        }
    }
    ~SparseGraph(){

    }

    int V(){//返回图中节点的数量
        return n;
    }

    int E(){//返回图中边的数量
        return m;
    }
    //在v与w节点之间加上一条边
    void addEdge(int v,int w){
        assert(v>=0&&v<n);
        assert(w>=0&&w<n);//防止越界
        g[v].push_back(w);//在v的邻接表中加入w节点
        if(v!=w&&!directed) g[w].push_back(v);//v与w不是同一个节点且图不是有向图,才需要
//执行改该步骤
        m++;
    }

    bool hasEdege(int v,int w) {//判断v节点与w节点之间是否存在边
        assert(v>=0&&v<n);
        assert(w>=0&&w<n);
        for(int i=0;i<g[v].size();i++){//遍历与v节点相连的节点
            if(g[v][i]==w) return true;//如果在邻接串中发现其中某一个节点与w节点相同,则表明
//v节点与w节点之间已经存在边
        }
        return false;
    }

对于邻接表来说,虽然能较好的节省空间,但是不能较好的处理平行边,即在添加边的时候不能防止添加平行边,即使在每次添加边之前,通过hasedge()判断是否有边来添加边可以防止平行边的产生,但是这样会大大减少图添加边的效率。因此,我们暂时允许平行边的产生。因此,这也算上邻接表的一个缺点吧。

 

 

如需获取本节的邻接矩阵和邻接表表示法完整代码,请点击此处

基础不牢?新手不友好?无人带路?关注《扬俊的小屋》公众号吧!

《图的两种表示方法》

《图的两种表示方法》

 

博客文章版权说明

 

 

 

第一条 本博客文章仅代表作者本人的观点,不保证文章等内容的有效性。

第二条 本博客部分内容转载于合作站点或摘录于部分书籍,但都会注明作/译者和原出处。如有不妥之处,敬请指出。

第三条 征得本博客作者同意的情况下,本博客的作品允许非盈利性引用,并请注明出处:“作者:____转载自____”字样,以尊重作者的劳动成果。版权归原作/译者所有。未经允许,严禁转载

第四条 对非法转载者,“扬俊的小屋”和作/译者保留采用法律手段追究的权利

第五条 本博客之声明以及其修改权、更新权及最终解释权均属“扬俊的小屋”。

第六条 以上声明的解释权归扬俊的小屋所有。

 

 

 

 

 

 

    原文作者:刘扬俊
    原文地址: https://blog.csdn.net/qq_19782019/article/details/79044766
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞