【数据结构】图以及图的遍历(深度遍历和广度遍历)

简介: 【数据结构】图以及图的遍历(深度遍历和广度遍历)


在数学中,图是描述于一组对象的结构,其中某些对象对在某种意义上是“相关的”。这些对象对应于称为顶点的数学抽象(也称为节点或点),并且每个相关的顶点对都称为边(也称为链接或线)。通常,图形以图解形式描绘为顶点的一组点或环,并通过边的线或曲线连接。 图形是离散数学的研究对象之一。 ------百度百科


6.1 基本术语


图:记为 G=( V, E )
其中:V 是G的顶点集合,是有穷非空集;
E 是G的边集合,是有穷集


6d95e90a165852f3c888e349ebf16c77_87edd1b5e061670e3651ae9dbc8db04f.png22946754e698cd6fdf665443cddbddfc_b2176fe437dc2385b02f0731766a9bb5.png

01dc016a8d9a89543f6ce9af9dd2b485_2d8148f47e2ebe7db55d3334d1e97bca.png

c2e76e0d66b82f19b1a6678ec4387b94_52f7df3e8443721bcc61fb84ad2977d8.png


6.2 存储结构


  • 图的特点:非线性结构
  • 图的顺序存储结构:无(多个定点,无序可言),但可以用数组描述元素间的关系。(设计为邻接矩阵)
  • 链式存储结构:可以用多重链表(邻接表,邻接多重表、十字链表)


下面结论面介绍:图的数组表示法(邻接矩阵)和链式表示法(邻接表)


图的数组表示法(邻接矩阵为例)


直接给出存储结构代码

#define MAX_VERTEX_NUM   100                                 /*最大顶点数设为100*/
/*---图的邻接矩阵表示---*/
typedef struct
{
     int vexs[MAX_VERTEX_NUM];                              /*顶点向量*/
     int  arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];             /*邻接距阵*/
     int  vexnum,arcnum;                                    /*顶点数和边数*/
     int kind;                                               /*---图的类型(1:有向图 0:无向图)---*/
}Mgraph;


解释

ede0d1adaf6c8d800367259cf6411510_6908e90c7578efb989a2c089b7b14ae8.png

给出边的矩阵表示形式


ad49c7996274c58a7386c86b73309f86_64cb2fd2f7bd10ae94cf794447e25388.png


27f5f5649000683e772f17b9be97f9b3_9e586277f65e4503dc0c2866a3738466.png

记住以上规律,对于有向图和无向图的创建,有很大帮助


图的链式存储结构(邻接表为例)


/*-------------- 图的邻接表--------*/
#define MAX_VERTEX_NUM  100
typedef struct ArcNode{
  int   adjvex;         //该弧所指向的顶点位置 
  struct ArcNode *nextarc; //指向下一条弧的指针 
  int weight;
}ArcNode;   //表结点 (狐)
typedef struct VNode{
  int data;         //顶点信息 
  ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 
}VNode;   //顶点
 typedef struct {
   Vnode adjlist [MAX_VERTEX_NUM]; /*邻接表*/
    int vexnum,arcnum;  //顶点数,边数
    int kind; 
 }ALGraph;  //图的邻接表


解释

985b53219238df24e5f1a6af020eda9a_912282f5b0755f592f0224a8c69e9692.png

b8fcbeeabe22f4bec49190019c98c7b5_4d6daca228795e7fdf8016d4d063017d.png

6.3 图的遍历


遍历定义:从已给的图中某一顶点出发,沿着一些边,访遍图中所有的顶点,且 使每个顶点仅被访问一次,就叫做图的遍历。

遍历实质:找每个顶点的邻接点的过程。

图的特点:图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。

解决思路:可设置一个辅助数组 visited [n ],用来标记每个被访问过的顶点。它的初始状态为0,在图的遍历过程中,一旦某一个顶点i 被访问,就立即改 visited [i]为1,防止它被多次访问。


图的常用遍历方式:1.深度优先搜索 2.广度优先搜索


图的遍历之深度优先遍历


深度优先遍历(Depth First Search),也有称为深度优先搜索,简称为DFS。


其原理如下:


  • 在访问图中某一起始顶点 v 后,由 v 出发,访问它的任一邻接顶点 w1;
  • 再从 w1 出发,访问与 w1邻接但还未被访问过的顶点 w2;
  • 然后再从 w2 出发,进行类似的访问,…
  • 如此进行下去,直至到达所有的邻接顶点都被访问过的顶点 u 为止。
  • 接着,退回一步,退到前一次刚访问过的顶点,看是否还有其它未被访问的邻接顶点。
  • 如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访问;
  • 如果没有,就再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止


https://ucc.alicdn.com/images/user-upload-01/img_convert/db2ae28f722c0721d79859729e63a32a.gif


https://ucc.alicdn.com/images/user-upload-01/img_convert/7b3d90a4765577fa0c03043291f4677b.gif


  • 注:作图做的不好,最好慢理解文字吧

不难从文字中看出,这是一个递归思想。

给出代码(邻接矩阵):

void DFS( Mgraph g,int v,int visited[])
 {/* 邻接矩阵存储,从顶点v出发,对图g进行深度优先搜索*/
  int j;
  printf("%d ",g.vexs[v]);
  visited[v]=1;   /*标识v被访问过*/
  for(j=0;j<g.vexnum;j++); /* */
  {
    if( g.arcs[v][j]==1&&visited[j]==0)/*j为v的邻接点,未被访问过*/
        DFS(g,j,visited);  /*从j出发递归调用DFS*/ 
   }/*for*/
 }/*DFS*/
 void DFSTraverse(Mgraph g)   
{ /*邻接矩阵 深度优先搜索*/
  int v;
  int visited[MAX_VERTEX_NUM];  
  for(v=0;v<g.vexnum;v++)  
    visited[v]=0; /*初始化visited数组*/ 
  for(v=0;v<g.vexnum;v++)
      if(visited[v]==0) DFS(g,v,visited);
              /*从未被访问过的v出发,DFS搜索*/
}


给出代码(邻接表):

void DFS(ALGraph *g,int v,int visited[])
 {/*从顶点v出发,对图g进行深度优先搜索*/
  ArcNode *p;
  int w;
  printf("%d ",g->adjlist[v].data);
  visited[v]=1;                 /*标识v被访问过*/
  p=g->adjlist[v].firstarc;      /*p指向第v个单链表的头指针*/
  while(p)
  {
    w=p->adjvex;              /*w为v的邻接点*/
    if(visited[w]==0)         /*若w未被访问*/
      DFS(g,w,visited);  /*从w出发递归调用DFS*/
    p=p->nextarc;              /*找v的下一个邻接点*/
   }/*while*/
 }/*DFS*/
 void DFSTraverse(ALGraph *g)
{ /*邻接表 深度优先搜索*/
  int v;
  int visited[MAX_VERTEX_NUM];
  for(v=0;v<g->vexnum;v++)
    visited[v]=0;  /*初始化visited数组*/
  for(v=0;v<g->vexnum;v++)
      if(visited[v]==0) DFS(g,v,visited);
              /*从未被访问过的v出发,DFS搜索*/
}


DFS算法的性能分析


DFS算法是一个递归算法,需要借助一个递归工作栈,故其空间复杂度为O ( V ) O(V)O(V)。

对于n个顶点e条边的图来说,邻接矩阵由于是二维数组,要查找每个顶点的邻接点需要访问矩阵中的所有元素,因此都需要O ( V 2 ) O(V^2)O(V 2 )的时间。而邻接表做存储结构时,找邻接点所需的时间取决于顶点和边的数量,所以是O ( V + E ) O(V+E)O(V+E)。 显然对于点多边少的稀疏图来说,邻接表结构使得算法在时间效率上大大提高。

对于有向图而言,由于它只是对通道存在可行或不可行,算法上没有变化,是完全可以通用的。


图的遍历之广度优先遍历


广度优先遍历(Breadth First Search),又称为广度优先搜索,简称BFS。

基本思想:——仿树的层次遍历过程。

遍历步骤:在访问了起始点v之后,依次访问 v的邻接点;然后再依次(顺序)访问这些点(下一层)中未被访问过的邻接点;直到所有顶点都被访问过为止。

广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有回退的情况。

因此,广度优先搜索不是一个递归的过程,其算法也不是递归的


给出代码(邻接矩阵):

void BFSTraverse(Mgraph *mgraph, int n)
{
    int v,w,u;
    int visited[n];
    int Q[n+1],r,f;  //Q为队列, 其中r,f分别为队尾指针和对头指针
//初始化visited数组和队列
    int i;
    for(i = 0;i<n;i++)
    {
        visited[i] = 0;
    }
    f = 0;
    r = 0;
    for(v = 0;v< mgraph->vexnum;v++)
    {
        if(!visited[v])
        {
            visited[v] = 1;//默认从v = 0 开始访问(第一访问)
            printf("%d ",mgraph->vexs[v]);//打印
            Q[r] = v;//入队列
            r = (r+1)%(n+1);
            while(f<r) //队列非空时
            {
                u = Q[f];f = (f+1)%(n+1);//出队并将出队数赋值给u
                for(w = 0;w<mgraph->vexnum;w++)
                {
                    if(mgraph->arcs[u][w]==1 && visited[w] == 0) //遍历该顶点所有邻接点,找该顶点是否存在没有访问过的邻接点
                    {//找到并输出 和入队
                        visited[w] = 1;
                        printf("%d ",mgraph->vexs[w]);
                        Q[r] = w;r = (r+1)%(n+1);
                    }
                }
            }
        }
    }
}


给出代码(邻接表):

void BFSTraverse(ALGraph *g ,int n)
{
   int  v,w,u;
   int Q[n+1];
   int visited[g->vexnum];
   int r = 0 ,f = 0;
   ArcNode *p;
   for(int i = 0;i<n+1;i++)   Q[i] = 0;
   for(v=0;v<g->vexnum;v++)   visited[v]=0 ;//初始化visited数组
                                                  //队列Q初始化
    for(v=0;v<g->vexnum ;v++)                       //从v出发广度优先搜索
   {    if (!visited[v])
  {
                    visited[v]=1;
    printf("%d ",v);                           //输出v
    Q[r] = v; r = (r+1)%(n+1);                          //v入队
    while(f<r || f>r)              //队列Q不空
    {
                          u = Q[f]; f = (f+1)%(n+1);// u出队
          p=g->adjlist[u].firstarc ;
         while(p)                                  //依次访问u的邻接点
         {               w=p->adjvex;
        if(!visited[w])
         {        visited[w]=1;
                   printf("%d ",w);
                  Q[r] = w; r = (r+1)%(n+1);     //w 入队
         }
         p=p->nextarc ;     //p指向下一个邻接点
       }//end of while
    }//end of while
  }//end of if
  }//end of for
}//end of BFSTravers


附———图的创建以及遍历完整代码(邻接矩阵和邻接表)


邻接矩阵(带注释)


点击查看代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_VERTEX_NUM   100                                 /*最大顶点数设为100*/
/*---图的邻接矩阵表示---*/
typedef struct
{
   int vexs[MAX_VERTEX_NUM];                              /*顶点向量*/
     int  arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];        /*邻接距阵*/
     int  vexnum,arcnum;                                    /*顶点数和边数*/
     int kind;                                                 /*---图的类型(1:有向图 0:无向图)---*/
}Mgraph;
bool InitMgraph(Mgraph *mgraph,int vexnum,int arcnum,int kind)
{
    if(!mgraph)
    {
        return false;
    }
    /*---初始化边的一些基本信息---*/
        mgraph->vexnum = vexnum;
        mgraph->arcnum = arcnum;
        mgraph->kind = kind;
        int i;
        int j;
        for(i=0;i<MAX_VERTEX_NUM;i++)
        {
            mgraph->vexs[i] = 0;
                for(j=0;j<MAX_VERTEX_NUM;j++)
            {
                mgraph->arcs[i][j] = 0;
            }
        }
    return true;
}
bool CreatMgraph(Mgraph *mgraph)
{
    if(!mgraph)
    {
        return false;
    }
    int i,j;
    for(i=0;i<mgraph->vexnum;i++)
    {
       mgraph->vexs[i] = i;
    }
    int count = 0;
    while(count<mgraph->arcnum)
    {
        scanf("%d,%d",&i,&j);
        if(mgraph->kind==0)
        {
            mgraph->arcs[i][j] = 1;
            mgraph->arcs[j][i] = 1;
        }
        else
        mgraph->arcs[i][j] = 1;
        count++;
    }
    return true;
}
void printMgraph(Mgraph *mgraph)
{
    printf("++++++++++++++++++++++++\n");
    printf("+++++图的基本信息+++++++\n");
    printf("+顶点数:%d             \n",mgraph->vexnum);
    printf("+边数  :%d             \n",mgraph->arcnum);
    printf("邻接矩阵:\n");
int i;
int j;
    for(i = 0;i<mgraph->vexnum;i++)
    {
        for(j = 0;j<mgraph->vexnum;j++)
        {
            printf(" %d",mgraph->arcs[i][j]);
        }
        printf("\n");
    }
}
 void DFS(Mgraph *m,int i,int visited[])
 {
     printf("%d",m->vexs[i]);
     visited[i] = 1;
     for(int j = 0;j<m->vexnum;j++)
     {
         if(m->arcs[i][j] == 1 && visited[j] == 0)
         {
             DFS(m,j,visited);
         }
     }
 }
 void DFST(Mgraph *m)
 {
     int visited[m->vexnum];
     for(int i = 0;i<m->vexnum;i++)
     {
         visited[i] = 0;
     }
     for(int i = 0;i<m->vexnum;i++)
     {
         if(visited[i]==0)
         DFS(m,i,visited);
     }
 }
 void BFST(Mgraph *m)
 {
     int u,v,w;
     int visited[m->vexnum];
     int Q[m->vexnum+1];
     int r,f;
     r = 0; f = 0;
     for(int i = 0;i<m->vexnum;i++)
     {
         visited[i] = 0;
     }
     for(int i = 0;i<m->vexnum;i++)
     {
         if(!visited[i])
         {
             printf("%d",m->vexs[i]);
             visited[i] = 1;
             Q[r] = i;  r = (r+1)%(m->vexnum+1);
             while(r!=f)
             {
                 u = Q[f];f = (f+1)%(m->vexnum+1);
                 for(int j = 0;j<m->vexnum;j++)
                 {
                     if(m->arcs[u][j] == 1 && visited[j] == 0)
                     {
                         visited[j] = 1;
                         printf("%d",m->vexs[j]);
                         Q[r] = j;  r = (r+1)%(m->vexnum+1);
                     }
                 }
             }
         }
     }
 }
int main()
{
     Mgraph *m1;
    m1 = (Mgraph*)malloc(sizeof(Mgraph));
    int num,s,kind;
    scanf("%d",&kind);
    scanf("%d,%d",&num,&s);
    InitMgraph(m1,num,s,kind);
    CreatMgraph(m1);
    DFST(m1);
    BFST(m1);
    return 0;
}


### 邻接表(带注释) 点击查看代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/*-------------- 图的邻接表--------*/
#define MAX_VERTEX_NUM  100
typedef struct ArcNode{
  int   adjvex;         //该弧所指向的顶点位置
  struct ArcNode *nextarc; //指向下一条弧的指针
  int weight;
}ArcNode;   //表结点 (狐)
typedef struct VNode{
  int data;         //顶点信息
  ArcNode *firstarc; //指向第一条依附该顶点的弧的指针
}Vnode;   //顶点
 typedef struct {
   Vnode adjlist [MAX_VERTEX_NUM]; /*邻接表*/
    int vexnum,arcnum;  //顶点数,边数
    int kind;
 }ALGraph;  //图的邻接表
bool InitALGraph(ALGraph *algmraph,int vexnum,int arcnum,int kind)
{
    if(!algmraph)
    {
        return false;
    }
    algmraph->vexnum = vexnum;
    algmraph->arcnum = arcnum;
    algmraph->kind = kind;
    for(int i = 0;i<algmraph->vexnum;i++)
    {
        algmraph->adjlist[i].firstarc = NULL;
        algmraph->adjlist[i].data = i;
    }
    return true;
}
bool CreatALGraph(ALGraph *algraph)
{
    if(!algraph)
    {
        return false;
    }
    int count = 0;
    ArcNode *p;
    int i,j;
    while(count<algraph->arcnum)
    {
        scanf("%d,%d",&i,&j);
        p = (ArcNode *)malloc(sizeof(ArcNode));
        p->adjvex = j;
        p->nextarc = algraph->adjlist[i].firstarc;
        algraph->adjlist[i].firstarc = p;
        if(algraph->kind==0)
        {
            p = (ArcNode *)malloc(sizeof(ArcNode));
            p->adjvex = i;
            p->nextarc = algraph->adjlist[j].firstarc;
            algraph->adjlist[j].firstarc = p;
        }
        count++;
    }
    return true;
}
void printALGraph(ALGraph *algraph)
{
    printf("++++++++++++++++++++++++++\n");
    printf("+边数:%d      定点数:%d+\n",algraph->vexnum,algraph->arcnum);
    printf("邻接表:\n");
    ArcNode *p;
    for(int i = 0;i<algraph->vexnum;i++)
    {
        p = algraph->adjlist[i].firstarc;
        while(p!=NULL)
        {
            printf("(%d,%d) ",i,p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
}
void DFS(ALGraph *m,int i, int visited[])
{
    ArcNode *p;
    printf("%d",m->adjlist[i].data);
    p = m->adjlist[i].firstarc;
    visited[i] = 1;
    while(p)
    {
        if(visited[p->adjvex] == 0)
        {
            DFS(m,p->adjvex,visited);
        }
        p = p->nextarc;
    }
}
void DFST(ALGraph *m)
{
    int visited[m->vexnum];
    for(int i = 0;i<m->vexnum;i++) visited[i] = 0;
    for(int i = 0;i<m->vexnum;i++)
    {
        if(!visited[i])
        {
            DFS(m,i,visited);
        }
    }
}
void BFST(ALGraph *m)
{
    int Q[m->vexnum+1];
    int r,f;
    r = 0;f = 0;
    int w;
    ArcNode *p;
    int visited[m->vexnum];
    for(int i = 0;i<m->vexnum;i++) visited[i] = 0,Q[i] = 0;
    for(int i = 0;i<m->vexnum;i++)
    {
        if(!visited[i])
        {
            printf("%d",i);
            Q[r] = i;r = (r+1)%(m->vexnum+1);
            visited[i] = 1;
            while(r!=f)
            {
                w = Q[f];f = (f+1)%(m->vexnum+1);
                p = m->adjlist[w].firstarc;
                    while(p)
                    {
                        if(!visited[p->adjvex])
                        {
                            printf("%d",m->adjlist[p->adjvex].data);
                            Q[r] = p->adjvex;r = (r+1)%(m->vexnum+1);
                            visited[p->adjvex] = 1;
                        }
                        p = p->nextarc;
                    }
            }
        }
    }
}
int main()
{
    ALGraph *al;
    al = (ALGraph*)malloc(sizeof(ALGraph));
    int kind;
    scanf("%d",&kind);
    int i,j;
    scanf("%d,%d",&i,&j);
    InitALGraph(al,i,j,kind);
    CreatALGraph(al);
    DFST(al);
    BFST(al);
   printALGraph(al);
    return 0;
}

相关文章
|
1月前
|
Go 索引
掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
|
1月前
|
算法
【算法与数据结构】二叉树(前中后)序遍历1
【算法与数据结构】二叉树(前中后)序遍历
|
1月前
|
算法
【算法与数据结构】二叉树(前中后)序遍历2
【算法与数据结构】二叉树(前中后)序遍历
|
5天前
【数据结构】二叉树(遍历,递归)
【数据结构】二叉树(遍历,递归
15 2
|
2天前
【数据结构】二叉树的三种遍历(非递归讲解)
【数据结构】二叉树的三种遍历(非递归讲解)
6 1
|
12天前
数据结构——二叉树的遍历【前序、中序、后序】
数据结构——二叉树的遍历【前序、中序、后序】
|
13天前
|
存储 算法
【数据结构与算法】8.二叉树的基本概念|前序遍历|中序遍历|后序遍历
【数据结构与算法】8.二叉树的基本概念|前序遍历|中序遍历|后序遍历
|
24天前
|
算法 API DataX
二叉树(下)+Leetcode每日一题——“数据结构与算法”“对称二叉树”“另一棵树的子树”“二叉树的前中后序遍历”
二叉树(下)+Leetcode每日一题——“数据结构与算法”“对称二叉树”“另一棵树的子树”“二叉树的前中后序遍历”
|
26天前
|
存储 机器学习/深度学习 移动开发
数据结构 第5 6 章作业 图 哈希表 西安石油大学
数据结构 第5 6 章作业 图 哈希表 西安石油大学
20 0
|
26天前
|
存储 机器学习/深度学习 算法
上机实验三 图的最小生成树算法设计 西安石油大学数据结构
上机实验三 图的最小生成树算法设计 西安石油大学数据结构
21 1