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

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


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


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月前
|
存储 编译器 C++
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
|
5月前
|
存储 算法
数据结构===图
数据结构===图
|
1月前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
24 0
|
2月前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
3月前
【数据结构】遍历二叉树(递归思想)-->赋源码
【数据结构】遍历二叉树(递归思想)-->赋源码
53 4
|
4月前
|
算法 Python
逆袭之路!用 Python 玩转图的 DFS 与 BFS,让数据结构难题无处遁形
【7月更文挑战第12天】图的遍历利器:DFS 和 BFS。Python 中,图可表示为邻接表或矩阵。DFS 沿路径深入,回溯时遍历所有可达顶点,适合找路径和环。BFS 层次遍历,先近后远,解决最短路径问题。两者在迷宫、网络路由等场景各显神通。通过练习,掌握这些算法,图处理将游刃有余。
65 3
|
4月前
|
存储 算法 Python
“解锁Python高级数据结构新姿势:图的表示与遍历,让你的算法思维跃升新高度
【7月更文挑战第13天】Python中的图数据结构用于表示复杂关系,通过节点和边连接。常见的表示方法是邻接矩阵(适合稠密图)和邻接表(适合稀疏图)。图遍历包括DFS(深度优先搜索)和BFS(广度优先搜索):DFS深入探索分支,BFS逐层访问邻居。掌握这些技巧对优化算法和解决实际问题至关重要。**
40 1
|
5月前
|
编译器 数据库 索引
数据结构篇:树形数据结构的基本概念及其遍历方法
数据结构篇:树形数据结构的基本概念及其遍历方法
80 0
|
5月前
|
存储 算法
【二叉树】数据结构——BST二叉树基本概念及算法设计(插入、删除、遍历操作)
【二叉树】数据结构——BST二叉树基本概念及算法设计(插入、删除、遍历操作)
|
5月前
|
存储
数据结构学习记录——如何建立图(邻接矩阵、邻接表-图节点的结构、创建并初始化、插入变、完整图的建立)
数据结构学习记录——如何建立图(邻接矩阵、邻接表-图节点的结构、创建并初始化、插入变、完整图的建立)
70 0