欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

图的深度与广度优先遍历

程序员文章站 2022-05-21 23:21:43
...

先介绍一下两种图的结构

邻接矩阵:

由一个一维数组存储图中的顶点,由一个二维数组表示元素之间的关系,用行和列以及其中的值表示两个顶点之间是否有边(弧),如第2行第3列的值为1,则表示第2个元素与第3个元素之间存在边。

typedef struct AMGraph
{
    int vex[ARRAYSIZE]={0};
    int arc[ARRAYSIZE][ARRAYSIZE];
    int vexnum=0,arcnum=0;//顶点个数、弧个数
    int type=0;//1表示无向图,2表示有向图,3表示无向网,4表示有向网
}AMGraph;
邻接表:

由一个结构体表示图结构,内部有一个顶点数组,存储图的顶点;还有一个表示图类型的成员和一个记录顶点数量的成员。
其中顶点数组的每一个结点也都是一个结构体,有两个成员,一个存储该顶点的值;另一个是一条单链表,表示与该顶点连接的边(弧)。
单链表的每一个结点也都是一个结构体,有三个成员,一个存储另一个顶点的下标(单链表所在的结构体,是一个顶点,而这条单链表的每一个结点都是与这个顶点相连的弧,所以该弧则需要记录其所连接的另一个顶点是谁);还有两个成员分别是用来连接顶点的next指针和存储权值的变量。

//定义弧结点
typedef struct ArcNode
{
    int adjvex=-1;//另一个顶点的下标
    struct ArcNode *next=nullptr;
    int weight=0;//权值
}ArcNode;
//定义顶点结点
typedef struct VexNode
{
    int data=0;//顶点数据
    ArcNode *AList=nullptr;//单链表头指针
}VexNode;
//定义邻接表
typedef struct ALGraph
{
    VexNode vex[ARRAYSIZE];//顶点数组
    int vexnum=0;//顶点数
    int type=0;//图的种类:1表示无向图,2表示有向图,3表示无向网,4表示有向网
}ALGraph;
图的遍历:

图的遍历是从某一顶点出发访遍图中其余顶点,且每个顶点只能被访问一次。
为了避免同一个顶点重复访问,在遍历的过程中,必须记下每个已访问过的顶点。为此可以设置一个辅助数组,其下标对应着顶点数组的下标,当数组元素值为假时,代表该顶点还未被访问,值为真则代表已访问。

深度优先遍历:

图的深度优先遍历类似树的先根遍历。假设初始状态是图中所有顶点未曾访问,此时从某一顶点v出发,访问此顶点,然后以此访问该顶点的所有邻接顶点,直到与v相连的顶点都被访问了一次;若此时图中还有顶点未访问,则再次重复上述过程,直到图中所有顶点都被访问到为止。
领接矩阵实现:

bool visited[ARRAYSIZE];//标志数组:true代表该顶点已访问,false代表该顶点未访问。
void DFS(AMGraph &G,int v)
{
    cout << G.vex[v] << endl;
    visited[v]=true;//标记已访问
    for(int i=0;i<G.vexnum;i++)//寻找下标为v的顶点的邻接顶点
    {
        if(G.arc[v][i]!=0 && G.arc[v][i]!=INT_MAX)
            if(visited[i]==false) DFS(G,i);//如果该邻接顶点尚未访问,对其调用DFS
    }
    //判断是否有向
    if(G.type==2 || G.type==4)
    {
        for(int i=0;i<G.vexnum;i++)
        {
            if(G.arc[i][v]!=0 && G.arc[v][i]!=INT_MAX)
               if(visited[i]==false) DFS(G,i);
        }
    }
}
void DFSTraverse(AMGraph &G)
{
    //初始化标志数组
    for(int i=0;i<G.vexnum;i++) visited[i]=false;
    //因为图中可能存在互不相连的子图,所以需要迭代判断每一个顶点是否已访问
    for(int i=0;i<G.vexnum;i++)
        if(visited[i]==false)
            DFS(G,i);//对尚未访问到的结点调用DFS
}

邻接表实现:

bool visited[ARRAYSIZE];//标志数组
void DFS(ALGraph &G,int v)
{
    cout << G.vex[v].data << endl;
    visited[v]=true;
    //访问该顶点的邻接顶点
    ArcNode *p=G.vex[v].AList;
    while(p!=nullptr)
    {
        if(visited[p->adjvex]==false)
            DFS(G,p->adjvex);
        p=p->next;
    }
    //若是有向图,搜索入度的邻接顶点
    if(G.type==2 || G.type==4)
        for(int i=0;i<G.vexnum;i++)
        {
            if(i==v) continue;
            p=G.vex[i].AList;
            while(p!=nullptr)
            {
                if(p->adjvex==v)
                {
                    if(visited[i]==false)
                        DFS(G,i);
                    break;
                }
                p=p->next;
            }
        }
}
void DFSTraverse(ALGraph &G)
{
    //初始化标志数组
    for(int i=0;i<G.vexnum;i++) visited[i]=false;
    //检测每一个顶点是否已访问
    for(int i=0;i<G.vexnum;i++)
        if(visited[i]==false) DFS(G,i);
}
广度优先遍历:

广度优先遍历类似树的层序遍历。先从图中某一点v出发,访问v,然后依次访问与v相连的顶点,接着再依次访问这些顶点的邻接顶点,以此类推,直到图中的顶点都被访问到为止。
和图的深度优先遍历一样,需要借助辅助数组记录顶点是否已访问;和树的层次遍历一样,需要借助队列结构,访问一个顶点并加入队列,顶点出队时将与之相连的顶点也加入队列。

领接矩阵实现:

void BFSTraverse(AMGraph &G)
{
    queue<int> Q;//辅助队列
    int pos=-1;//用于获取队头元素
    //初始化标志数组
    for(int i=0;i<G.vexnum;i++) visited[i]=false;
    
    for(int i=0;i<G.vexnum;i++)
    {
        if(visited[i]==false)
        {
            cout << G.vex[i] << endl;
            visited[i]=true;//标记已访问
            Q.push(i);//顶点下标入队
            while(!Q.empty())
            {
                pos=Q.front();
                //访问队头顶点的邻接顶点
                for(int j=0;j<G.vexnum;j++)
                    if(G.arc[pos][j]!=0 && G.arc[pos][j]!=INT_MAX)
                    {
                        if(visited[j]==false)
                        {
                            cout << G.vex[j] << endl;
                            visited[j]=true;
                            Q.push(j);
                        }
                    }
                //判断是否有向
                if(G.type==2 || G.type==4)
                {
                    for(int j=0;j<G.vexnum;j++)
                        if(G.arc[j][pos]!=0 && G.arc[j][pos]!=INT_MAX)
                           if(visited[j]==false)
                           {
                               cout << G.vex[j] << endl;
                               visited[j]=true;
                               Q.push(j);
                           }
                }
                //队头出队
                Q.pop();
            }//while
        }//if
    }//for
}

邻接表实现:

void BFSTraverse(ALGraph &G)
{
    queue<int> Q;//辅助队列
    int pos=-1;//负责获取队头
    ArcNode *p=nullptr;//搜索单链表的指针
    //初始化标记数组
    for(int i=0;i<G.vexnum;i++) visited[i]=false;
    //检测每一个顶点是否已访问
    for(int i=0;i<G.vexnum;i++)
        if(visited[i]==false)
        {
            cout << G.vex[i].data << endl;
            visited[i]=true;//标记已访问
            Q.push(i);//入队
            while(!Q.empty())
            {
                pos=Q.front();
                //访问队头的邻接顶点
                p=G.vex[pos].AList;
                while(p!=nullptr)
                {
                    if(visited[p->adjvex]==false)
                    {
                        cout << G.vex[p->adjvex].data << endl;
                        visited[p->adjvex]=true;
                        Q.push(p->adjvex);
                    }
                    p=p->next;
                }
                if(G.type==2 || G.type==4)
                for(int j=0;j<G.vexnum;j++)
                {
                    if(j==pos) continue;
                    p=G.vex[j].AList;
                    while(p!=nullptr)
                    {
                        if(p->adjvex==pos)
                        {
                            if(visited[j]==false)
                            {
                                cout << G.vex[j].data << endl;
                                visited[j]=true;
                                Q.push(j);
                            }
                            break;
                        }
                        p=p->next;
                    }
                }
                //队头出队
                Q.pop();
            }//while
        }//if
}