zoukankan      html  css  js  c++  java
  • 基于邻接矩阵存储的图的深度优先遍历和广度优先遍历

    图的存储结构相比较线性表与树来说就复杂很多,对于线性表来说,是一对一的关系,所以用数组或者链表均可简单存放。树结构是一对多的关系,所以我们要将数组和链表的特性结合在一起才能更好的存放。

    那么我们的图,是多对多的情况,另外图上的任何一个顶点都可以被看作是第一个顶点,任一顶点的邻接点之间也不存在次序关系。

    仔细观察以下几张图,然后深刻领悟一下:

    因为任意两个顶点之间都可能存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系(内存物理位置是线性的,图的元素关系是平面的)。

    如果用多重链表来描述倒是可以做到,但在几节课前的树章节我们已经讨论过,纯粹用多重链表导致的浪费是无法想像的(如果各个顶点的度数相差太大,就会造成巨大的浪费)。

    邻接矩阵(无向图)

    考虑到图是由顶点和边或弧两部分组成,合在一起比较困难,那就很自然地考虑到分为两个结构来分别存储。

    顶点因为不区分大小、主次,所以用一个一维数组来存储是狠不错的选择。

    而边或弧由于是顶点与顶点之间的关系,一维数组肯定就搞不定了,那我们不妨考虑用一个二维数组来存储。

    图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。

    我们可以设置两个数组,顶点数组为vertex[4]={V0,V1,V2,V3},边数组arc[4][4]为对称矩阵(0表示不存在顶点间的边,1表示顶点间存在边)。

    对称矩阵:所谓对称矩阵就是n阶矩阵的元满足a[i][j]=a[j][i](0<=i,j<=n)。即从矩阵的左上角到右下角的主对角线为轴,右上角的元与左下角相对应的元全都是相等的。

    有了这个二维数组组成的对称矩阵,我们就可以很容易地知道图中的信息:

    1. 要判定任意两顶点是否有边无边就非常容易了;
    2. 要知道某个顶点的度,其实就是这个顶点Vi在邻接矩阵中第i行(或第i列)的元素之和;
    3. 求顶点Vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点咯。

    邻接矩阵(有向图)

    无向图的边构成了一个对称矩阵,貌似浪费了一半的空间,那如果是有向图来存放,会不会把资源都利用得很好呢?

    可见顶点数组vertex[4]={V0,V1,V2,V3},弧数组arc[4][4]也是一个矩阵,但因为是有向图,所以这个矩阵并不对称,例如由V1到V0有弧,得到arc[1][0]=1,而V0到V1没有弧,因此arc[0][1]=0。

    另外有向图是有讲究的,要考虑入度和出度,顶点V1的入度为1,正好是第V1列的各数之和,顶点V1的出度为2,正好是第V1行的各数之和。

    邻接矩阵(网)

    在图的术语中,我们提到了网这个概念,事实上也就是每条边上带有权的图就叫网。

    下面以此无向图为例,使用邻接矩阵存储,并实现深度优先遍历和广度优先遍历:

    代码如下:

    #include <stdio.h>
    
    #define MaxVex        100            //最大顶点数
    #define INFINITY    65535        //表示∞
    #define TRUE        1
    #define    FALSE        0
    typedef char        VertexType;    //顶点类型
    typedef    int            EdgeType;    //权值类型
    typedef int            Bool;
    Bool    visited[MaxVex];
    
    typedef struct {
        VertexType    vexs[MaxVex];            //顶点数组
        EdgeType    arc[MaxVex][MaxVex];    //邻接矩阵
        int    numVertexes, numEdges;            //当前图中的结点数以及边数
    }MGraph;
    
    
    //广度优先遍历需要的循环队列
    typedef struct {
        int    data[MaxVex];
        int    front, rear;
    }Queue;
    
    
    /****************************************/
    //队列的相关操作
    
    //初始化
    void InitQueue(Queue *Q)
    {
        Q->front = Q->rear = 0;
    }
    
    //入队
    void EnQueue(Queue *Q, int e)
    {
        if ((Q->rear+1)%MaxVex == Q->front)
            return ;
    
        Q->data[Q->rear] = e;
        Q->rear = (Q->rear+1)%MaxVex;
    }
    
    //判空
    Bool QueueEmpty(Queue *Q)
    {
        if (Q->front == Q->rear)
            return TRUE;
        else
            return FALSE;
    }
    
    //出队
    void DeQueue(Queue *Q, int *e)
    {
        if (Q->front == Q->rear)
            return ;
        
        *e = Q->data[Q->front];
        Q->front = (Q->front+1)%MaxVex;
    }
    /****************************************/
    
    
    //建立图的邻接矩阵
    void CreateMGraph(MGraph *G)
    {
        int i, j, k, w;
    
        printf("输入顶点数和边数: ");
        scanf("%d%d", &G->numVertexes,&G->numEdges);
        fflush(stdin);
    
        printf("==============================
    ");
        printf("输入各个顶点:
    ");
        for (i=0; i<G->numVertexes; ++i)
        {
            printf("顶点%d: ",i+1);
            scanf("%c", &G->vexs[i]);
            fflush(stdin);
        }
    
        for (i=0; i<G->numVertexes; ++i)
        {
            for (j=0; j<G->numVertexes; ++j)
                G->arc[i][j] = INFINITY;
        }
    
        printf("==============================
    ");
        for (k=0; k<G->numEdges; ++k)
        {
            printf("输入边(vi, vj)中的下标i和j和权W: ");
            scanf("%d%d%d", &i,&j,&w);
            G->arc[i][j] = w;
            G->arc[j][i] = G->arc[i][j];
        }
    }
    
    
    //输出
    void DisMGraph(MGraph *G)
    {
        int i, j, k;
        k = G->numVertexes;
        for (i=0; i<k; ++i)
        {
            for (j=0; j<k; ++j)
            {
                printf("%5d ", G->arc[i][j]);
            }
            putchar('
    ');
        }
    }
    
    
    /****************************************/
    //图的深度优先遍历
    void DFS(MGraph G, int i)
    {
        int j;
        visited[i] = TRUE;
        printf("%c ",    G.vexs[i]);
    
        for (j=0; j<G.numVertexes; ++j)
        {
            if (G.arc[i][j]!=INFINITY  &&  !visited[j])
                DFS(G, j);
        }
    }
    
    void DFSTraverse(MGraph G)
    {
        int i;
        for (i=0; i<G.numVertexes; ++i)
            visited[i] = FALSE;
    
        for (i=0; i<G.numVertexes; ++i)
        {
            if (!visited[i])
                DFS(G, i);
        }
    
    }
    
    
    //图的广度优先遍历
    void BFSTraverse(MGraph *G)
    {
        int i, j;
        Queue Q;
    
        for (i=0; i<G->numVertexes; ++i)
            visited[i] = FALSE;
    
        InitQueue(&Q);
    
        for (i=0; i<G->numVertexes; ++i)
        {
            if (!visited[i])
            {
                visited[i] = TRUE;
                printf("%c ", G->vexs[i]);
                EnQueue(&Q, i);
    
                while (!QueueEmpty(&Q))
                {
                    DeQueue(&Q, &i);
                    for (j=0; j<G->numVertexes; ++j)
                    {
                        if (!visited[j] && G->arc[i][j]!=INFINITY)
                        {
                            visited[j] = TRUE;
                            printf("%c ", G->vexs[j]);
                            EnQueue(&Q, j);
                        }
                    }
                }
            }
        }
    }
    /****************************************/
    
    //程序入口
    int main(){
        MGraph G;
    
        CreateMGraph(&G);
    
        printf("
    图的深度优先遍历为: ");
        DFSTraverse(G);    
    
        printf("
    图的广度优先遍历为: ");
        BFSTraverse(&G);
    
        printf("
    ");
    
        return 0;
    }

    运行结果截图:

  • 相关阅读:
    帧同步资料收集
    随机数种子问题
    【转】 DOTA2中的伪随机及其lua实现
    C++ 异常机制分析
    细说new与malloc的10点区别
    static关键字总结
    C++11 并发编程基础(一):并发、并行与C++多线程
    论一个程序员的自我修养
    gSoap的多线程程序
    面试常见问题:
  • 原文地址:https://www.cnblogs.com/zhangming-blog/p/5412085.html
Copyright © 2011-2022 走看看