算法笔记---图

本文详细介绍了图的两种常见存储结构——邻接矩阵和邻接表,包括它们的定义和实现。同时,深入探讨了图的遍历算法,如广度优先搜索(BFS)和深度优先搜索(DFS),并展示了如何应用于单源最短路径问题、生成树构造以及有向图路径判断。此外,还给出了图的邻接矩阵和邻接表之间的转换算法以及图的基本操作如查找邻接顶点。最后,通过实例展示了如何利用DFS和BFS判断无向图是否为树以及是否存在特定路径。
摘要由CSDN通过智能技术生成

1.图的邻接矩阵存储结构

#define MAXVERTEXNUM 100
typedef char VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上全权值的数据类型
typedef struct MGraph{
    VertexType Vex[MAXVERTEXNUM]; //顶点表
    EdgeType Edge[MAXVERTEXNUM][MAXVERTEXNUM]; //邻接矩阵,边表
    int vexnum,arcnum; //图的当前顶点数和弧数
}MGraph;

2.图的邻接表存储结构

#define MAXVERTEXNUM 100
typedef struct ArcNode{ //边表节点
    int adjvex; //该弧所指向的顶点的位置
    struct ArcNode *next; //指向下一条弧的指针
    InfoType info; //网的边权值
}ArcNode;

typedef struct VNode{ //顶点表节点
    VertexType data; //顶点信息
    ArcNode *first; //指向第一条依附该顶点的弧的指针
}VNode,AdjList[MAXVERTEXNUM];

typedef struct ALGraph{
    AdjList vertices; //邻接表
    int vexnum,arcnum; //图的顶点数和弧数
}ALGraph;

3.图的遍历

3.1广度优先搜索(BFS)

类似于树的层次遍历

bool visited[MAX_VERTEX_NUM]; //访问标记数组

void BFSTraverse(Graph G){ //对图G进行广度优先遍历
    queue<int> q; //初始化辅助队列
    for(int i=0;i<G->vexnum;i++) //将标记数组初始化
        visited[i]=false;
    for(int i=0;i<G->vexnum;i++) //从0号顶点开始遍历
        BFS(G,i,q);
}

void BFS(Graph G,int v,Queue<int> &queue){
    visit(v); //访问初始顶点v
    visited[v]=true; //标记v已经访问
    queue.push(v); //将v入队列
    while(!queue.empty()){
        v=queue.front(); //将队列中第一个出队
        queue.pop();
        for(int w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){ //检测v所有邻接点
            if(!visited(w)){ //w为v的尚未访问的邻接顶点
                visit(w); //访问w
                visited[w]=true; //标记w已经访问
                queue.push(w); //将w入队
            }
        }
    }
}
3.1.1BFS算法求单源最短路径问题
void BFS_MIN_Distance(Graph G,int u){
    //d表示从u到i节点的最短路径,-1为没有路径
    int d[vexnum];
    for(int i=0;i<G->vexnum;i++)
        d[i]=-1; //初始化路径长度
    queue<int> q;
    d[u]=0;
    visited[u]=true;
    q.push(u);
    while(!q.empty()){ //BFS算法主过程
        u=q.front(); //队头元素出队
        q.pop();
        for(int w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w)){
            if(!visited[w]){ //w为u未访问过的邻接节点
                d[w]=d[u]+1; //路径长度+1
                visited[w]=true; //设置w已经访问
                q.push(w); //将其入队
            }
        }
    }
}
3.1.2广度优先生成树

3.2深度优先搜索(DFS)

类似于树的先序遍历

bool visited[MAX_VERTEX_NUM];//标记访问数组

void DFSTraverse(Graph G){ //对图G进行深度优先遍历
    for(int i=0;i<G->vexnum;i++) //将标记数组初始化
        visited[i]=false;
    for(int i=0;i<G->vexnum;i++) //从v=0开始遍历
        if(!visited[i]) 
            DFS(G,i);
}

void DFS(Graph G,int v){ //从顶点v出发,深度优先遍历图
    visit(v); //访问顶点v
    visited[v]=true; //将v标记为访问过
    for(int w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
        if(!visited[w]) //w为v尚未访问的邻接节点
            DFS(G,w); //递归访问
    }
}

4.图的基本操作

取x邻接顶点y的下一个邻接顶点的函数NextNeighbor(G,x,y)

//邻接矩阵作为存储结构
int NextNeighbor(MGraph G,int v,int w){
    if(v!=-1&&w!=-1){
        for(int col=w+1;col<G->vexnum;col++){
            if(G->Edge[v][col]>0&&G->Edge[x][col]<maxWeight)
                return col;
        }
    }
    return -1;
}

//邻接表作为存储结构
int NextNeighbor(MGraph G,int v,int w){
    if(v!=-1){
        ArcNode *a=G->vertices[v]->first; //对应边链表的第一个边节点
        while(!p&&p->adjvex!=w){ //寻找邻接顶点w
            p=p->next;
        }
        if(!p&&!p->adjvex)
            return p->next->adjvex; //返回下一个邻接顶点
    }
}

图对应的算法题

邻接表&&邻接矩阵

1.写出从图的邻接表表示转成邻接矩阵表示的算法

void convert(ALGraph &G,int arcs[M][N]){
    for(int i=0;i<n;i++){ //一次遍历各顶点表节点为头的边链表
        p=(G->vertices[i]).first; //去除顶点i的第一条出边
        while(p!=null){ //遍历边链表
            arc[i][p->adjvex]=1; 
            p=p->next; //取出下一条边
        }
    }
}

2.2021统考真题

已知无向连通图G由顶点集V和边集E组成,|E|>0,当G中度为奇数的顶点个数不大于2的偶数时,G存在包含所有边且长度为|E|的路径(称为EL路径)。设图G采用邻接矩阵存储,类型如下:

typedef struct{
    int numVertices,numEdges;
    char VerticesList[MAXV];
    int Edge[MAXV][MAXV];
}MGraph;

设计算法int IsExistEL(MGraph G),判断G是否存在EL路径,若存在,则返回1,否则返回0

int IsExistEL(MGraph &G){
    int verticesCount=0,edgesCount=0;
    for(int i=0;i<G->numVertices;i++){
        for(int j=i+1;j<G->numVertices;j++){
            if(G->Edge[i][j]==1)
                edgesCount++; //计算当前顶点的度
        }
        if(edgesCount%2!=0) //对度为奇数的顶点计数
            verticesCount++;
        edgesCount=0;
    }
    return (verticesCount==0||verticesCount==2)?1:0;
}

DFS&&BFS

1.判断一个无向图是否为一棵树

bool isTree(Graph &G){
    for(int i=0;i<G->vexnum;i++)
        visited[i]=false;
    int vNum=0,eNum=0;
    DFS(G,1,vNum,eNum,visited);
    //一颗无向图G是一棵树的条件是G必须是无回路的连通图或由n-1条边的连通图
    if(vNum==G->vexnum&&eNum=2*(vNum-1)) return true;
    else return false;
}

void DFS(Graph &G,int v,int &vNum,int &eNum,int visited[]){ 
    //深度优先遍历,统计访问过的顶点数和边数,通过vNum和eNum返回
    visited[v]=true; //访问标记
    vNum++;
    int w=FirstNeighbor(G,v); //取v的第一个邻接顶点
    while(w!=-1){ //当邻边顶点存在
        eNum++; //边数+1
        if(!visited[w]){
            DFS(G,w,vNum,eNum,visited);
            w=NextNeighbor(G,v,w);
        }
    }
}

2.写出图的DFS的非递归算法(采用邻接表形式)

void DFS_Recurrence(ALGraph G,int v){
    int visited[G->vexnum];
    for(int i=0;i<G->vexnum;i++) //初始化visited,其用于判断顶点是否如果栈
        visited[i]=false;
    stack<int> s;
    s.push(v);
    visited[v]=true; //将第一个节点标记为已入栈
    while(!s.empty()){
        v=s.top(); //取得栈顶节点,并弹出
        s.pop();
        visit(v); //访问该节点
        for(ArcNode *p=G->vertices[v]->first;p!=null;p=p->next){ //遍历该点的邻接弧
            int w=p->adjvex; //获取该弧指向的下一顶点号
            if(!visited(w)){ //如果该顶点没有入过栈
                s.push(w); //将其入栈并标记
                vistited(w)=true;
            }
        }
    }
}

3.分别采用DFS和BFS判别以邻接表方式存储的有向图中是否存在由顶点vi到vj(i!=j)的路径

//DFS
void IsHaveRoute_DFS(ALGraph G,int v,int j,bool &result){
    if(v==j) //用result来标识是否有路径
        result=true;
    int visited[G->vexnum];
    for(int i=0;i<G->vexnum;i++) //初始化visited,其用于判断顶点是否访问过
        visited[i]=false;
    visited[i]=true;
    for(ArcNode *p=G->vertices[i]->first;p!=null;p=p->next){
        int w=p->adjvex;
        if(!visited[w]&&!result){ //当前节点未访问过且到目前位置没有找到路径,则继续遍历
            IsHaveRoute_DFS(G,w,j,result);
        }
    }
}

//BFS
//其实就相当于BFS遍历,只不过在遍历过程中加入判断条件当前遍历节点是否和vj相等
void IsHaveRoute_BFS(ALGraph G,int v,int j){
    int visited[G->vexnum];
    for(int i=0;i<G->vexnum;i++) //初始化visited,其用于判断顶点是否访问过
        visited[i]=false;
    queue<int> q;
    q.push(v);
    visited[v]=true;
    while(!q.empty()){
        v=q.front();
        q.pop();
        for(ArcNode *p=G->vertices[v]->first;p!=null;p=p->next){ 
            int w=p->adjvex;
            if(w==j) return true;
            if(!visited[w]{
                visited[w]=true;
                q.push(w);
            }
        }
    }
}

4.图采用邻接表表示,设计一个算法,输出从顶点vi到顶点vj的所有简单路径

void FindPath(ALGraph G,int v,int j,string &path){
    path.append(v); //用一个字符串存放路径
    visited(v)=true; //访问标记
    if(v=j){
        cout<<path; //输出路径
    }
    for(ArcNode *p=G->vertices[v]->first;p!=null;p=p->next){ //DFS的主要步骤
        int w=p->adjvex;
        if(!visited[w])
            FindPath(G,w,j,path);
    }
    //回退
    path.erase(path.end()-1); //路径回退到上个节点
    visited[v]=false; //使该顶点可用
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值