美文网首页
数据结构与算法-图的深度和广度优先遍历

数据结构与算法-图的深度和广度优先遍历

作者: 收纳箱 | 来源:发表于2020-05-02 10:20 被阅读0次

    1.基础数据结构

    1.1 邻接矩阵

    #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 numVertices, numEdges;      /* 图中当前的顶点数和边数 */
    } MGraph;
    

    1.2 邻接表

    #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 EdgeNode {  /* 边表结点 */
        int adjvex;                  /* 邻接点域,存储该顶点对应的下标 */
        EdgeType weight;             /* 用于存储权值,对于非网图可以不需要 */
        struct EdgeNode *next; /* 链域,指向下一个邻接点 */
    } EdgeNode;
    
    typedef struct VertexNode { /* 顶点表结点 */
        VertexType data;                /* 顶点域,存储顶点信息 */
        EdgeNode *firstEdge;        /* 边表头指针 */
    } VertexNode, AdjList[MAXVEX];
    
    typedef struct {
        AdjList adjList;
        int numVertices, numEdges; /* 图中当前顶点数和边数 */
    } graphAdjList, *GraphAdjList;
    

    2. 深度优先遍历

    深度优先,是因为先从一个顶点出发,找到每个顶点在表中最近未被访问的顶点,继续递归,一条路走到黑,达到最深。

    核心:

    • 需要一个额外数组记录顶点是否被访问过。
    • 不管是邻接矩阵还是邻接表,其实每个每个点位都被访问过。
    • 每次递归返回(出栈),会从上次的位置继续往后遍历,查找是否有未访问的顶点,继续深度优先遍历。

    2.1 邻接矩阵实现

    Boolean visited[MAXVEX]; // 是否访问过的标志数组
    void DFS(MGraph G, int i) {
        visited[i] = TRUE; // 先标记被访问过
        printf("%c ", G.vexs[i]); // 打印被访问的顶点
        
        for (int j = 0; j < G.numVertices; j++) // 遍历顶点i的邻接矩阵
            if(G.arc[i][j] == 1 // 确认i和j顶点相连
               && !visited[j])  // 确认没有被访问
                DFS(G, j);
    }
    
    void DFSTraverse(MGraph G) {
        // 1.初始化
        for (int i = 0; i < G.numVertices; i++)
            visited[i] = FALSE;
        // 2.遍历所有顶点,非连通图DFS完还有没有标记完,需要遍历确认
        for (int i = 0; i < G.numVertices; i++)
            if(!visited[i])
                DFS(G, i);
    }
    

    2.2 邻接表实现

    顶点数组的遍历和邻接矩阵一样,只是把邻接矩阵每行的数组遍历,改为了链表的遍历。

    Boolean visited[MAXVEX]; // 是否访问过的标志数组
    void DFS(GraphAdjList GL, int i) {
        visited[i] = TRUE; // 先标记被访问过
        printf("%c ", GL->adjList[i].data); // 打印被访问的顶点
        
        EdgeNode *p = GL->adjList[i].firstEdge; // 链表的开头
        while (p->next) {
            if (!visited[p->adjvex]) { // 连接的顶点还没被访问过,邻接表里面都是相连的的顶点
                DFS(GL, p->adjvex);
            }
            p = p->next;
        }
    }
    
    void DFSTraverse(GraphAdjList GL) {
        // 1.初始化
        for (int i = 0; i < GL->numVertices; i++)
            visited[i] = FALSE;
        // 2.遍历所有顶点,非连通图DFS完还有没有标记完,需要遍历确认
        for (int i = 0; i < GL->numVertices; i++) {
            if(!visited[i])
                DFS(GL, i);
        }
    }
    

    3. 广度优先遍历

    广度优先,是因为先从一个顶点出发,找到每个顶点相连接的未访问顶点入队。有点每到一个顶点呼朋唤友的感觉。

    核心:

    • 需要一个额外数组记录顶点是否被访问过。
    • 类似于树的层序遍历。
    • 利用了队列数据结构作为辅助。
    • 不管是邻接矩阵还是邻接表,其实每个每个点位都被访问过。

    3.1 邻接矩阵实现

    Boolean visited[MAXVEX]; /* 访问标志的数组 */
    void BFSTraverse(MGraph G) {
        // 1.初始化
        for (int i = 0; i < G.numVertices; i++)
            visited[i] = FALSE;
        Queue queue;
        InitQueue(&queue);
        // 2.遍历所有顶点
        int idx;
        for (int i = 0; i < G.numVertices; i++) {
            if (!visited[i]) { // 确认没有被访问
                visited[i] = TRUE; // 先标记被访问过
                printf("%c ", G.vexs[i]); // 打印被访问的顶点
                EnQueue(&queue, i); // 3.入队
                while (!QueueEmpty(queue)) { // 队列中还有顶点
                    DeQueue(&queue, &idx); // 4.出队
                    for (int j = 0; j < G.numVertices; j++) { // 把出队元素相连的未访问顶点全部入队
                        if (G.arc[idx][j] == 1  // 确认i和j顶点相连
                            && !visited[j]) { // 确认没有被访问
                            visited[j] = TRUE; // 先标记被访问过
                            printf("%c ", G.vexs[j]); // 打印被访问的顶点
                            EnQueue(&queue, j); // 入队
                        }
                    }
                }
            }
        }
    }
    

    3.2 邻接表实现

    同样,只是把邻接矩阵每行的遍历,替换为链表的遍历。

    Boolean visited[MAXSIZE]; /* 访问标志的数组 */
    void BFSTraverse(GraphAdjList GL) {
        // 1.初始化
        for (int i = 0; i < GL->numVertexes; i++)
            visited[i] = FALSE;
        Queue queue;
        InitQueue(&queue);
        // 2.遍历所有顶点
        int idx;
        EdgeNode *p;
        for (int i = 0; i < GL->numVertexes; i++) {
            if (!visited[i]) { // 确认没有被访问
                visited[i] = TRUE;  // 先标记被访问过
                printf("%c ", GL->adjList[i].data); // 打印被访问的顶点
                EnQueue(&queue, i); // 入队
                while (!QueueEmpty(queue)) {
                    DeQueue(&queue, &idx); // 4.出队
                    p = GL->adjList[idx].firstEdge;
                    while (p) { // 把出队元素相连的未访问顶点全部入队
                        idx = p->adjvex; // 得到顶点的下标
                        if (!visited[idx]) { // 相连顶点未访问过
                            visited[idx] = TRUE; // 先标记被访问过
                            printf("%c ", GL->adjList[idx].data); // 打印被访问的顶点
                            EnQueue(&queue, idx); // 入队
                        }
                        p = p->next;
                    }
                }
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:数据结构与算法-图的深度和广度优先遍历

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