美文网首页
数据结构与算法学习 (12)图的遍历

数据结构与算法学习 (12)图的遍历

作者: 暱稱已被使用 | 来源:发表于2020-05-20 21:42 被阅读0次

    图的遍历是指从图中的任意顶点出发,对图中的所有顶点访问一次且只访问一次。图的遍历操作和树的遍历操作功能相似。图的遍历是图的一种基本操作,图的许多其他操作都是建立在遍历操作的基础之上的
    1.深度优先遍历
    深度优先思想:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

    深度优先是一个递归的过程。首先,选定一个出发点后进行遍历,如果有邻接的未被访问过的节点则继续前进。若不能继续前进,则回退一步再前进,若回退一步仍然不能前进,则连续回退至可以前进的位置为止。重复此过程,直到所有与选定点相通的所有顶点都被遍历。
    深度优先是递归过程,带有回退操作,因此需要使用栈存储访问的路径信息。当访问到的当前顶点没有可以前进的邻接顶点时,需要进行出栈操作,将当前位置回退至出栈元素位置。

    当图采用邻接矩阵存储时,由于矩阵元素个数为n2,因此时间复杂度就是O(n2)。
    当图采用邻接表存储时,邻接表中只是存储了边结点(e条边,无向图也只是2e个结点),加上表头结点为n(也就是顶点个数),因此时间复杂度为O(n+e)。

    邻接矩阵的深度优先遍历:

    typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
    typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
    
    typedef char VertexType; /* 顶点类型应由用户定义 */
    typedef int EdgeType; /* 边上的权值类型应由用户定义 */
    
    #define MAXSIZE 9 /* 存储空间初始分配量 */
    #define MAXEDGE 15
    #define MAXVEX 9
    #define INFINITYC 65535
    
    typedef struct
    {
       VertexType vexs[MAXVEX]; /* 顶点表 */
       EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
       int numVertexes, numEdges; /* 图中当前的顶点数和边数 */
    }MGraph;
    
    /*4.1 构建一个邻接矩阵*/
    void CreateMGraph(MGraph *G)
    {
       int i, j;
       
       //1. 确定图的顶点数以及边数
       G->numEdges=15;
       G->numVertexes=9;
       
       /*2.读入顶点信息,建立顶点表 */
       G->vexs[0]='A';
       G->vexs[1]='B';
       G->vexs[2]='C';
       G->vexs[3]='D';
       G->vexs[4]='E';
       G->vexs[5]='F';
       G->vexs[6]='G';
       G->vexs[7]='H';
       G->vexs[8]='I';
       
       /*3. 初始化图中的边表*/
       for (i = 0; i < G->numVertexes; I++)
       {
           for ( j = 0; j < G->numVertexes; j++)
           {
               G->arc[i][j]=0;
           }
       }
       
       /*4.将图中的连接信息输入到边表中*/
       G->arc[0][1]=1;
       G->arc[0][5]=1;
       
       G->arc[1][2]=1;
       G->arc[1][8]=1;
       G->arc[1][6]=1;
       
       G->arc[2][3]=1;
       G->arc[2][8]=1;
       
       G->arc[3][4]=1;
       G->arc[3][7]=1;
       G->arc[3][6]=1;
       G->arc[3][8]=1;
       
       G->arc[4][5]=1;
       G->arc[4][7]=1;
       
       G->arc[5][6]=1;
       
       G->arc[6][7]=1;
       
       /*5.无向图是对称矩阵.构成对称*/
       for(i = 0; i < G->numVertexes; I++)
       {
           for(j = i; j < G->numVertexes; j++)
           {
               G->arc[j][i] =G->arc[i][j];
           }
       }
    }
    
    /*4.2 DFS遍历*/
    Boolean visited[MAXVEX]; /* 访问标志的数组 */
    //1. 标识顶点是否被标记过;
    //2. 选择从某一个顶点开始(注意:非连通图的情况)
    //3. 进入递归,打印i点信息,标识; 边表
    //4. [i][j] 是否等于1,没有变遍历过visted
    void DFS(MGraph G,int i){
       //1.
       visited[i] = TRUE;
       printf("%c",G.vexs[I]);
       
       //2.0~numVertexes
       for(int j = 0; j < G.numVertexes;j++){
           if(G.arc[i][j] == 1 && !visited[j])
               DFS(G, j);
       }
    }
    
    void DFSTravese(MGraph G){
       //1.初始化
       for(int i=0;i<G.numVertexes;i++){
           visited[i] = FALSE;
       }
       
       //2.某一个顶点
       for(int i = 0;i<G.numVertexes;i++){
           if(!visited[I]){
               DFS(G, i);
           }
       }
    }
    

    邻接表的深度优先遍历:

    #define MAXSIZE 9 /* 存储空间初始分配量 */
    #define MAXEDGE 15
    #define MAXVEX 9
    #define INFINITYC 65535
    
    typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
    typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
    
    typedef char VertexType; /* 顶点类型应由用户定义 */
    typedef int EdgeType; /* 边上的权值类型应由用户定义 */
    
    /* 邻接矩阵结构 */
    typedef struct
    {
       VertexType vexs[MAXVEX]; /* 顶点表 */
       EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
       int numVertexes, numEdges; /* 图中当前的顶点数和边数 */
    }MGraph;
    
    /* 邻接表结构****************** */
    typedef struct EdgeNode /* 边表结点 */
    {
       int adjvex;    /* 邻接点域,存储该顶点对应的下标 */
       int weight;        /* 用于存储权值,对于非网图可以不需要 */
       struct EdgeNode *next; /* 链域,指向下一个邻接点 */
    }EdgeNode;
    
    typedef struct VertexNode /* 顶点表结点 */
    {
       int in;    /* 顶点入度 */
       char data; /* 顶点域,存储顶点信息 */
       EdgeNode *firstedge;/* 边表头指针 */
    }VertexNode, AdjList[MAXVEX];
    
    typedef struct
    {
       AdjList adjList;
       int numVertexes,numEdges; /* 图中当前顶点数和边数 */
    }graphAdjList,*GraphAdjList;
    
    /*4.1 构建一个邻接矩阵*/
    void CreateMGraph(MGraph *G)
    {
       int i, j;
       
       //1. 确定图的顶点数以及边数
       G->numEdges=15;
       G->numVertexes=9;
       
       /*2.读入顶点信息,建立顶点表 */
       G->vexs[0]='A';
       G->vexs[1]='B';
       G->vexs[2]='C';
       G->vexs[3]='D';
       G->vexs[4]='E';
       G->vexs[5]='F';
       G->vexs[6]='G';
       G->vexs[7]='H';
       G->vexs[8]='I';
       
       /*3. 初始化图中的边表*/
       for (i = 0; i < G->numVertexes; I++)
       {
           for ( j = 0; j < G->numVertexes; j++)
           {
               G->arc[i][j]=0;
           }
       }
       
       /*4.将图中的连接信息输入到边表中*/
       G->arc[0][1]=1;
       G->arc[0][5]=1;
       
       G->arc[1][2]=1;
       G->arc[1][8]=1;
       G->arc[1][6]=1;
       
       G->arc[2][3]=1;
       G->arc[2][8]=1;
       
       G->arc[3][4]=1;
       G->arc[3][7]=1;
       G->arc[3][6]=1;
       G->arc[3][8]=1;
       
       G->arc[4][5]=1;
       G->arc[4][7]=1;
       
       G->arc[5][6]=1;
       
       G->arc[6][7]=1;
       
       /*5.无向图是对称矩阵.构成对称*/
       for(i = 0; i < G->numVertexes; I++)
       {
           for(j = i; j < G->numVertexes; j++)
           {
               G->arc[j][i] =G->arc[i][j];
           }
       }
    }
    
    /*4.2 利用邻接矩阵构建邻接表*/
    void CreateALGraph(MGraph G,GraphAdjList *GL){
       
       //1.创建邻接表,并且设计邻接表的顶点数以及弧数
       *GL = (GraphAdjList)malloc(sizeof(graphAdjList));
       (*GL)->numVertexes = G.numVertexes;
       (*GL)->numEdges = G.numEdges;
       
       //2. 从邻接矩阵中将顶点信息输入
       for (int i = 0; i < G.numVertexes; i++) {
           //顶点入度为0
           (*GL)->adjList[i].in = 0;
           //顶点信息
           (*GL)->adjList[i].data = G.vexs[i];
           //顶点边表置空
           (*GL)->adjList[i].firstedge = NULL;
       }
       
       //3. 建立边表
       EdgeNode *e;
       for (int i = 0; i < G.numVertexes; i++) {
           for (int j = 0; j < G.numVertexes; j++) {
               if (G.arc[i][j] == 1) {
                
                   //创建边表中的邻近结点 i->j
                   e = (EdgeNode *)malloc(sizeof(EdgeNode));
                   //邻接序号为j
                   e->adjvex = j;
                   //将当前结点的指向adjList[i]的顶点边表上
                   e->next = (*GL)->adjList[i].firstedge;
                   (*GL)->adjList[i].firstedge = e;
                   //顶点j 上的入度++;
                   (*GL)->adjList[j].in++;
                   
    //                //创建边表中的邻近结点 j->i
    //                e = (EdgeNode *)malloc(sizeof(EdgeNode));
    //                //邻接序号为j
    //                e->adjvex = I;
    //                //将当前结点的指向adjList[i]的顶点边表上
    //                e->next = (*GL)->adjList[j].firstedge;
    //                (*GL)->adjList[j].firstedge = e;
    //                //顶点j 上的入度++;
    //                (*GL)->adjList[i].in++;
               }
           }
       }
    }
    
    
    Boolean visited[MAXSIZE]; /* 访问标志的数组 */
    /* 邻接表的深度优先递归算法 */
    void DFS(GraphAdjList GL, int i)
    {
       EdgeNode *p;
       visited[i] = TRUE;
       
       //2.打印顶点 A
       printf("%c ",GL->adjList[i].data);
       
       p = GL->adjList[i].firstedge;
       
       //3.
       while (p) {
           if(!visited[p->adjvex])
               DFS(GL,p->adjvex);
           
           p = p->next;
       }
       
    }
    
    /* 邻接表的深度遍历操作 */
    void DFSTraverse(GraphAdjList GL)
    {
       //1. 将访问记录数组默认置为FALSE
       for (int i = 0; i < GL->numVertexes; i++) {
           /*初始化所有顶点状态都是未访问过的状态*/
           visited[i] = FALSE;
       }
    
       //2. 选择一个顶点开始DFS遍历. 例如A
       for(int i = 0; i < GL->numVertexes; I++)
           //对未访问过的顶点调用DFS, 若是连通图则只会执行一次.
           if(!visited[I])
               DFS(GL, i);
    }
    

    2.广度遍历
    类似于树的按层次遍历的过程。假设从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问,直至图中所有已被访问的邻接点都被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为起始点,重复上述过程,直至所有顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以v为起始点,由近至远,依次访问和v有路径相通且路径长度为1,2,···的顶点。

    如上图中的c,首先访问v1和v1的邻接点v2和v3,然后依次访问v2的邻接点v4和v5及v3的邻接点v6和v7,最后访问v4的邻接点v8。得到访问序列为:v1 →v2→v3→v4→v5→v6→v7→v8。和深度优先搜索类似,在遍历的过程中也需要一个访问标志数组。并且为了顺次访问路径长度为1,2,3···的顶点,需要附设队列以存储已被访问的路径长度为1,2,···的顶点。

    邻接矩阵的广度优先遍历代码:

    /* 循环队列的顺序存储结构 */
    typedef struct
    {
        int data[MAXSIZE];
        int front;        /* 头指针 */
        int rear;        /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
    }Queue;
    
    /* 初始化一个空队列Q */
    Status InitQueue(Queue *Q)
    {
        Q->front=0;
        Q->rear=0;
        return  OK;
    }
    
    /* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
    Status QueueEmpty(Queue Q)
    {
        if(Q.front==Q.rear) /* 队列空的标志 */
        return TRUE;
        else
        return FALSE;
    }
    
    /* 若队列未满,则插入元素e为Q新的队尾元素 */
    Status EnQueue(Queue *Q,int e)
    {
        if ((Q->rear+1)%MAXSIZE == Q->front)    /* 队列满的判断 */
        return ERROR;
        Q->data[Q->rear]=e;            /* 将元素e赋值给队尾 */
        Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, */
        /* 若到最后则转到数组头部 */
        return  OK;
    }
    
    /* 若队列不空,则删除Q中队头元素,用e返回其值 */
    Status DeQueue(Queue *Q,int *e)
    {
        if (Q->front == Q->rear)            /* 队列空的判断 */
        return ERROR;
        *e=Q->data[Q->front];                /* 将队头元素赋值给e */
        Q->front=(Q->front+1)%MAXSIZE;    /* front指针向后移一位置, */
        /* 若到最后则转到数组头部 */
        return  OK;
    }
    /*1.2.1 邻接矩阵广度优先遍历-代码实现*/
    Boolean visited[MAXVEX]; /* 访问标志的数组 */
    void BFSTraverse(MGraph G){
        
        int temp = 0;
        
        //1.
        Queue Q;
        InitQueue(&Q);
        
        //2.将访问标志数组全部置为"未访问状态FALSE"
        for (int i = 0 ; i < G.numVertexes; i++) {
            visited[i] = FALSE;
        }
        
        //3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
        for (int i = 0 ; i < G.numVertexes; i++) {
            
            if(!visited[i]){
                visited[i] = TRUE;
                printf("%c  ",G.vexs[i]);
                
                //4. 入队
                EnQueue(&Q, i);
                while (!QueueEmpty(Q)) {
                    //出队
                    DeQueue(&Q, &i);
                    for (int j = 0; j < G.numVertexes; j++) {
                        if(G.arc[i][j] == 1 && !visited[j])
                        {    visited[j] = TRUE;
                            printf("%c   ",G.vexs[j]);
                            EnQueue(&Q, j);
                        }
                    }
                }
            }
            
        }
        
        
    }
    
    

    邻接表的广度优先遍历代码:

    /*1.2.2 邻接表广度优先遍历*/
    Boolean visited[MAXSIZE]; /* 访问标志的数组 */
    void BFSTraverse(GraphAdjList GL){
        
        //1.创建结点
        EdgeNode *p;
        
        Queue Q;
        InitQueue(&Q);
        
    
        //2.将访问标志数组全部置为"未访问状态FALSE"
        for(int i = 0; i < GL->numVertexes; i++)
            visited[i] = FALSE;
        
        //3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
        for(int i = 0 ;i < GL->numVertexes;i++){
            //4.判断当前结点是否被访问过.
            if(!visited[i]){
                visited[i] = TRUE;
                //打印顶点
                printf("%c ",GL->adjList[i].data);
                
                EnQueue(&Q, i);
                while (!QueueEmpty(Q)) {
                    DeQueue(&Q, &i);
                    p = GL->adjList[i].firstedge;
                    while (p) {
                        //判断
                        if(!visited[p->adjvex]){
                            visited[p->adjvex] = TRUE;
                             printf("%c ",GL->adjList[p->adjvex].data);
                            EnQueue(&Q, p->adjvex);
                        }
                        p = p->next;
                    }
                }
                
            }
        }
        
    }
    

    相关文章

      网友评论

          本文标题:数据结构与算法学习 (12)图的遍历

          本文链接:https://www.haomeiwen.com/subject/tnsoohtx.html