图的搜索算法

数据结构与算法

图的遍历:

1.深度优先搜索:

深度优先遍历v0所在的连通子图

void DepthFirstSearch(Graph g,int v0)
{
    visit(v0);
    visited[v0]=True;
    w=FirstAdjVertex(g,v0);
   //如果这个邻接点存在
    while(w!=-1)
    {
        //如果这个邻接点没有被访问过
        if(!visited[w]) DepthFirstSearch(g,w);
        w=NextAdjVertex(g,v0,w);//取v0的下一个顶点做DFS
    }
}

深度优先遍历图g的算法描述

#define True 1
#define False 0
int visited[MAX_VERTEX_NUM];
void TraverseGraph(Graph g)
{
    for(v=0;v<g.vexnum;v++)
        visited[v]=False;
    for(v=0;v<g.vexnum;v++)
        if(!visited[v]) DepthFirstSearch(g,v);
}

深度优先搜索可以帮我们判断这个图是否为连通图,连通分量的数目以及点到点的简单路径。

采用邻接矩阵的DepthFirstSearch

void DepthFirstSearch(AdjMaxtri g,int v0)
{
    visit(v0);
    visited[v0]=True;
    for(vj=0;vj<g.vexnum;vj++)
        if(!visited[vj]&&g.arcs[v0][vj].adj==1)
            DepthFirstSearch(g,vj);
}

2.广度优先搜索算法:

类似于树的层次遍历

void BreadthFirstSearch(Graph g,int v0)
{
    visit(v0);
    visited[v0]=True;
    InitQueue(&Q);
    EnterQueue(&Q,v0);
    while(!Empty(Q))
    {
        DeleteQueue(&Q,&v);
        w=FirstAdj(g,v);
        while(w!=-1)
        {
            if(!visited(w))
            {
                visit(w);
                visited[w]=True;
                EnterQueue(&Q,w);
            }
            w=NextAdj(g,v,w);
        }
    }
}

图的应用——最小生成树

连通图:在无向图里面,任意两点之间都有路径可以相通。

完全图:在无向图里面,任意两个顶点之间有直达的边相连。

生成树:一个连通图的生成树是指一个极小连通子图,含有图中的全部n个顶点,但只有足以构成一棵树的n-1条边。

扫描二维码关注公众号,回复: 3430845 查看本文章

构造网的一棵最小生成树,即:在e条带权的边中选取n-1条边(不构成回路),使“权值之和”为最小。

最小生成树算法:

1.Prim普里姆算法

取图中任意一个顶点v作为生成树的根,之后往生成树上添加新的顶点w,在添加的顶点w和已经在生成树上的顶点v之间必定存在一条边,该边的权值在所有连通顶点v和w之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有n个顶点为止。

一般情况下所添加的顶点应满足条件:

在生成树的构造过程中,图中n个顶点分属两个集合:

已经落在生成树上的顶点集U尚未落在生成树上的顶点集V-U,则应在所有连通U中顶点和v-u中顶点的边中选取权值最小的边。

struct
{
    vertexData adjvex;
    int lowcost;
}closedge[MAX_VERTEX_NUM]
MiniSpanTree_Prim(AdjMatrix gn,VertexData u)
{
    k=LocateVertex(gn,u);
    closedge[k].lowcost=0;
    for(i=0;i<gn.vexnum;i++)
        if(i!=k)
        {
            closedge[i].adjvex=u;
            closedge[i].lowcost=gn.arcs[k][i].adj;
        }
    for(e=1;e<=gn.vexnum-1;e++)
    {
        k0=Minium(closedge);
        u0=closedge[k0].adjvex;
        v0=gn.vexs[k0]
        printf(u0,v0);
        closedge[k0].lowcost=0;
        for(i=0;i<vexnum;i++)
            if(gn.arcs[k0][i].adj<closedge[i].lowcost)
            {
                closedge[i].lowcost=gn.arcs[k0][i].adj;
                closedge[i].adjvex=v0;
            }
    }
}
时间复杂度O(n的平方)
与图的顶点数相关

Kruskal克里斯卡尔算法

为使生成树上边的权值之和达到最小,则应使生成树中每一条边的权值尽可能地小

先构造一个只含n个顶点的子图SG,然后从权值最小的边开始,若它的添加不使SG中产生回路,则在SG上加上这条边,如此重复,直至加上n-1条边为止

1.所有的边按权值从小到大排序(排序算法:堆排序)

2.顶点集合状态,边如果加入则顶点加入。(判断回路)

3.最小生成树边的集合

  • 图的生成树不唯一,从不同的顶点出发进行遍历,可以得到不同的生成树;
  • 既是从相同的顶点出发,在选择最小边时,可能有多条同样的边可选,此时任选其一。

Prim算法适应于稠密图(顶点多)

3.图的拓扑排序

程序的数据流图,每个顶点代表一个活动,弧

//各顶点入度的函数
void FindID(AdjList G,int indegree[MAX_VERTEX_NUM])
{
    int i;
    ArcNode *p;
    for(i=0;i<G.vexnum;i++)
        indegree[i]=0;
    for(i=0;i<G.vexnum;i++)
    {
        p=G.vertexes[i].firstarc;
        while(p!=NULL)
        {
            indegree[p->adjvex]++;
            p=p->nextarc;
        }
    }
}
//将入度为0的结点入队
//整个算法实现过程
int TopoSort(AdjList G)
{
    Queue Q;/*队列*/
    int indegree[MAX_VERTEX_NUM];
    int i,count,k;
    ArcNode *p;
    FindID(G,indegree);
    InitStack(&S);
    for(i=0;i<G.vexnum;i++)
        if(indegree[i]==0)
            EnterQueue(&Q,i);
        count=0;
    while(!StackEmpty(S))//判断队列是否为空
    {
        DeleteQueue(&Q,&i);
        printf("%c",G.vertex[i].data);
        count++;
        p=G.vertexes[i].firstarc;
        while(p!=NULL)
        {
            k=p->adjvex;
            indegree[k]--;
            if(indegree[k]==0)
                EnterQueue(&S,k);
            p=p->nextarc;
        }
    }
    if(count<G.vexnum)
        return(Error);
    else return(Ok);
}

4.图的关键路径

在有向网中,如果用顶点表示事件,用有向边表示活动,边上的权值表示活动持续的时间,则称这样的有向网为弧表示活动的网(AOE-网)。对仅有一个开始点和一个完成点的工程,可用AOE-网来表示,它可用来估算工程完成的时间。

AOE网的特征:

1.只有在某顶点所代表的事件发生后,从该顶点发出的所有有向边所代表的活动才能开始;

2.只有在进入某一顶点的各有向边所代表的活动均已完成,该顶点所代表的事件才能发生。

关键路径:

从有向图的源点到汇点的最长路径,即关键路径。

关键活动持续时间的总和(关键路径的长度)

ve(j):事件(顶点)的最早发生时间

vl(k):从顶点k到汇点的最短路径长度,即事件(顶点)的最迟发生时间

关键路径:ee(i)=ve(j)

​ el(i)=vl(k)-dut(

//按拓扑顺序求出每个事件的最早发生时间
//按逆拓扑序列求出每个事件的最迟发生时间
//计算每个活动的最早开始时间和最晚发生时间
//找出关键活动,即ee(a_i)=el(a_i)
//改造拓扑排序算法
int TopoSort_Ve(AdjList G,Stack *T)
{
    Queue Q;
    int indegree[MAXVEX],ve[MAXVEX];
    int i,count=0,k;
    ArcNode *p;
    FindID(G,indegree);
    InitQueue(&Q);InitStack(&T);
    for(i=0;i<G.vexnum;i++)
        if(indegree[i]==0)
            EnterQueue(&Q,i);
    for(i=0;i<G.vexnum;i++)
        ve[i]=0;
    while(!IsEmpty(Q))
    {
        DeleteQueue(&Q,&i);Push(T,i);count++;
        p=G.vertex[i].head;
        while(p!=NULL)
        {
            k=p->adjvex;
            if(--indegree[k]==0)
                EnterQueue(&Q,k);
            if(ve[j]+p->weight>ve[k])
                ve[k]=ve[j]+p->weight;
            p=p->next;
        }
    }
    if(count<G.vexnum) return 0;
    else return 1;
}
Int CriticalPath(AdjList G)
{
    ArcNode *p;
    int i,j,k,a,eei,eli,flag=0;
    int vl[MAXVEX];
    Stack S;
    if(!TopoSort_Ve(G,&T))
        return(0);
    for(i=0;i<G.vexnum;i++)
        vl[i]=ve[G.vexnum-1];
    while(!IsEmpty(&T))
    {
        pop(T,&j);
        p=G.vertex[j].head;
        while(p)
        {
            k=p->adjvex;
            a=p->weight;
            if(vl[k]-a<vl[j])
                vl[j]=vl[k]-a;
            p=p->next;
        }
    }
    for(i=0;i<vexnum;i++)
    {
        p=G.vertex[i].head;
        while(p)
        {
            k=p->adjvex;
            a=p->weight;
            eei=ve[j];
            eli=vl[k]-a;
            if(eei==eli) flag=1;//活动的最早开始时间和最晚发生时间
            p=p->next;
        }
    }
}
时间复杂度?

5.图的单源最短路径

1.从单源点到其余各点的最短路径:Dijkstra

依最短路径的长度递增的次序求得各条路径。其中,从源点v0到顶点vi的最短路径是v0到各点最短路径集合中长度最短者。

2.每一对顶点之间的最短路径:Floyd


贪心算法的内容后续更新~

猜你喜欢

转载自blog.csdn.net/nicezheng_1995/article/details/81675425