数据结构知识整理 - 遍历图

版权声明: https://blog.csdn.net/Ha1f_Awake/article/details/84466090

主要内容

1. 以邻接矩阵为存储结构的深度优先搜索遍历

2. 以邻接表为存储结构的深度优先搜索遍历

1. 以邻接矩阵为存储结构的广度优先搜索遍历

2. 以邻接表为存储结构的广度优先搜索遍历


图的遍历算法是实现图的其他算法的基础,图的遍历方式有两种:深度优先搜索遍历广度优先搜索遍历

深度优先搜索遍历类似于树的先序遍历(根节点→左子树→右子树),借助了结构以实现递归广度优先搜索遍历则类似于层次遍历,借助于队列结构实现。

因为图的任意两个顶点都有可能存在联系,所以在访问某个顶点后,可能沿着某条路径又会回到访问过的顶点。为了避免同一顶点被访问多次,在遍历图的过程中,必须用一个数组visited[vexnum]记录每个访问过的顶点。

邻接矩阵为存储结构的遍历,时间复杂度为O(n²);以邻接表为存储结构的遍历,时间复杂度为O(n+e)


深度优先搜索

深度优先搜索(Depth First Search,DFS)遍历是树的先序遍历的推广。

遍历过程:

(1)从图中的某个顶点开始访问;

(2)若顶点存在未被访问的邻接顶点,则继续访问它的其中一个邻接顶点;

(3)重复步骤(2)直至某个顶点不存在未被访问的邻接顶点;

(4)从步骤(3)的某个顶点开始回溯访问,若回溯访问的邻接顶点存在未被访问的邻接顶点,则再执行步骤(2)(3)(4);

(5)重复步骤(2)(3)(4)直至所有顶点均被遍历,NOT EXISTS visted[n] 的值为0。

这种一条路走到底,走不通再返回的特征正是“深度”一词的体现。

需要提一下的是,深度优先算法的代码并没有体现“回溯”的过程,“回溯”体现在标记数组visited[vexnum]中,遍历图的所有顶点一个深入、判断、覆盖的过程。

1. 以邻接矩阵为存储结构的深度优先搜索遍历

​
#define TRUE 1                          /*已访问*/
#define False 0                         /*未访问*/

void DFS_AMG(AMGraph &G, int v1)         /*v1为起始顶点*/
{
    int visited[G.vexnum] = {0};        /*定义记录已访问顶点的数组*/
                                        /*定义局部数组时,只将首元素初始化为0,其余元素自动设0*/
    cout<<v1;                           /*以输出语句表示遍历,对顶点进行其他操作则换为函数调用*/
    int i = LocateVex(G, v1);           /*确定顶点v1对应的编号*/
    visited[i] = TRUE;                  /*记录已访问顶点*/

    for(int v2 = 1; v2 <= G.vexnum && v2 != v1; v2++)       /*v2图中的另一个顶点*/
    {
        int j = LocateVex(G, v2);                           /*同样需要确定v2的编号*/

        /*当v1与v2有关联且v2未被访问,递归执行DFS_AMG()*/
        if(G.arcs[i][j] != 0 && visited[j] = False)              
            DFS_AMG(G,v2);
    }
}

​

理解递归过程:

(1)邻接矩阵中确定起始顶点v1所代表的第i行

(2)第i行中找到第一个值不为0的结点,该结点在第j列

(3)判断visited[j]的值是否为FALSE,若是,将注意力转到第j行

(4)在第j行中进行与步骤(1)(2)(3)相同的过程;

(5)若第i行中还存在第二个值不为0的结点,该结点在第k列,则在执行完所有从第i行、第j列(arcs[i][j])开始的过程后,再执行从arcs[i][k]开始的所有过程,始终按照“先被访问的顶点的邻接点,先于后被访问的顶点的邻接点”的原则;

(6)在步骤(5)中的顺序执行同样发生在每一个顶点上,即每一行中,所以深度优先遍历是一个深入、判断、覆盖(不断将visited[n]的值填为TRUE)的过程

提前路过的圈毛君:“虽然已经很努力去解释了,可自己看还是感觉有点拗口,希望大家能看明白叭_(:з」∠)_”

2. 邻接表为存储结构的深度优先搜索遍历

​
void DFS_ALG(ALGraph &G, int v1)
{
    int visited[G.vexnum] = {0};    

    cout<<v1;
    visited[v1-1] = TRUE;               /*第i个顶点vi的编号为i-1*/
                                        /*在邻接矩阵中还需要vi的合理性,所以用LocateVex()函数来求编号*/

    ArcNode *p = G.ALs[v1-1].nextarcnode; /*指针p指向头结点v1指向的下一个边结点*/

    while(p != NULL)                    /*若边结点不为空*/
    {
        int v2 = p->anothervex;         /*通过边结点确定v1的一个邻接顶点*/

        if(visited[v2-1] = FALSE)
            DFS_ALG(G,v2);
        
        p = p->nextarcnode;             /*应用链式结构就不需要像邻接矩阵那样在循环语句中一列一列判断,给指针赋值为下一个指针就好*/
    }
}

​

理解代码的思路跟上面类似。

由深度优先的遍历过程可以得到一棵以起始顶点v1为根结点的树,这棵树称作深度优先生成树


 

广度优先搜索

广度优先搜索(Breadth First Search,BFS)遍历类似于树的层次遍历

广度优先的过程与深度优先类似,唯一的区别是广度优先是一层一层地遍历,而不会从某个顶点开始一直往下遍历,直到不符合遍历的条件。

广度优先以横向遍历为先,即访问顶点v1后,顺序访问v1的邻接顶点v2,v3,然后先顺序访问2的邻接顶点,再顺序访问v3的邻接顶点,如此类推。

深度优先利用结构实现递归,广度优先则需要队列来实现每一层的顺序执行:

(1)v1入队,访问顶点v1,并在visited[vexnum]将顶点v1对应的元素值归TRUE

(2)检查v1的邻接顶点,v2和v3顺序入队

(3)v1出队

(4)访问顶点v2,检查v2的邻接顶点并使它们顺序入队

(5)v2出队,访问顶点v3;

(6)访问顶点v3,检查v3的邻接顶点并使它们顺序入队

(7)之后的入队、出队过程如此类推。

*利用队列“先来先服务”(FCFS)的性质,在编写代码时可以将思路转化为先求队列,再遍历。

1. 以邻接矩阵为存储结构的广度优先搜索遍历

int visited[G.vexnum];              /*全局变量和静态变量初始化时会自动被设置为0*/
LinkQueue Q;                        /*定义链队Q*/
InitQueue(Q);                       /*初始化链队Q*/

void AL_EnQueue(LinkQueue &Q, int v1)        /*从顶点v1开始,将所有顶点逐个入队*/
{
    int i = LocateVex(G, v1);

    for(int v2 = 1; v2 <= G.vexnum; v2++)    /*先使v1未被访问的邻接顶点顺序入队*/ 
    {
        int j = LocateVex(G, v2);

        if(G.arcs[i][j] != 0 && visited[j] = FALSE)          
        {
            EnQueue(Q, v2);
            visited[j] = TRUE;               /*标记*/
        }
    }

    for(int v2 = 1; v2 <= G.vexnum; v2++)    /*再在v1的邻接顶点上递归*/ 
    {
        int j = LocateVex(G, v2);

        if(G.arcs[i][j] != 0 && visited[j] = FALSE)          
            AL_QnQueue(Q, v2);        
    }
}

void BFS_AMG(AMGraph &G, int v1)    
{
    EnQueue(Q, v1);                          /*v1入队*/

    int i = LocateVex(G, v1);
    visited[i] = TRUE;

    AL_EnQueue(Q, v1);                       /*所有顶点入队*/

    cout<<GetHead(Q);                        /*访问链队的头结点*/
    DeQueue(Q);                              /*头结点出队*/

}

2. 以邻接表为存储结构的广度优先搜索遍历

int visited[G.vexnum];             
LinkQueue Q;                
InitQueue(Q);                  

void AL_EnQueue(LinkQueue &Q, int v1)      
{
    ArcNode *p = G.ALs[v1-1].nextarcnode;

    while(p != NULL)
    {
        int v2 = p->anothervex;
    
        if(visited[v2-1] = FALSE)
            EnQueue(Q, v2); 

        p = p->nextarcnode;
    }               

    while(p != NULL)
    {
        int v2 = p->anothervex;
    
        if(visited[v2-1] = FALSE)
            AL_EnQueue(Q, v2); 

        p = p->nextarcnode;
    }               

}


void BFS_AMG(AMGraph &G, int v1)    
{
    EnQueue(Q, v1);   
                     
    visited[v1-1] = TRUE;

    AL_EnQueue(Q, v1);                   

    cout<<GetHead(Q);                     
    DeQueue(Q);                           

}

​

由深度优先的遍历过程可以得到一棵以起始顶点v1为根结点的树,这棵树称作深度优先生成树

猜你喜欢

转载自blog.csdn.net/Ha1f_Awake/article/details/84466090