图的应用

1 最小生成树

1 概念及注意

  • 针对的是带权无向图
  • 如果本身就是一棵树,则它的最小生成树就是本身
  • 只有连通图才有生成树,非连通图只有生成森林

2 实现算法

1 Prim算法

从某一顶点开始构建生成树;每次将代价最小的新顶点纳入生成树,直到所有顶点都纳入为止

时间复杂度:O(|V|²)

适合边稠密的图

2 Kruskal算法

每次选择一条权值最小的边,使这条边的两头连通(已经连通的就不选)直到所有的结点都连通

时间复杂度:O(|E|log2|E|)

适合边稀疏而顶点较多的图

wbOhBF.png
wbOXnO.png

2 最短路径

分类

单源最短路径:BFS(无权图),Dijkstra算法(无权/带权)

各顶点之间的最短路径:Floyd算法(无权/带权)

当图是带权图时,一条路径上所有边的权值之和称为带权路径长度

1 BFS求最短路径

void BFS_Min_Distance(Graph G,int u)
{
    for(int i=0;i<G.vexnum;i++)
    {
        visited[i]=false;
        d[i]=∞;
        p[i]=-1;
    }
    d[u]=0;visited[u]=true;
    Queue Q;
    EnQueue(Q,u);
    for(w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w))
    {
        DeQueue(Q,u);
        if(!visited[u])
        {
            d[w]=d[u]+1;
            p[w]=u;
            visited=true;
            EnQueue(Q,w);
        }
    }
}

2 DijKstra算法

  • 不适用于带有负权值的图
  • 时间复杂度O(|V|²)
    wqe4bQ.png

3 Floyd算法

若 A(k-1)[i] [j]> A(k-1)[i] [k]+ A(k-1)[k] [j]

则 A(k)[i] [j]=A(k-1)[i] [k]+ A(k-1)[k] [j]

Path(k)=k

否则A(k)[i] [j]和Path(k)保持原值

算法代码实现

void Floyd_Min_Distance(Graph G)
{
    for(k=0;k<n;k++)
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
            {
                if(A[i][j]>A[i][k]+A[k][j])
                    A[i][j]=A[i][k]+A[k][j];
            }
}

开销:

时间复杂度:O(|V|³)

空间复杂度:O(|V|²)

可以用于负权值的图,但是不可以用于带负权值的回路

wjiWfx.png

4 总结

wjFwEd.png

5 有向无环图(DAG:Directed Acyclic Graph)描述表达式

wjmgWd.png

6 拓扑排序

1.AOV网(Activity On Vertex Network):用顶点表示活动的网

2. 拓扑排序代码实现

bool TopoLogicalSort(Graph G)
{
    Stack S;
    for(int i=0;i<G.vexnum;i++)
    {
        if(indegree[i]==0)//入度为0的进栈
            S.Push(i);
    }
    int count=0;
    while(!S.isEmpty())
    {
        i=S.Pop();//栈顶元素出栈
        print[count++]=i;//记录出栈序列,即为拓扑排序
        for(p=G.verticle[i].firstarc;p;p=p->nextarc)//遍历所有与i顶点所连的边
        {
            v=p->adjvex;//v是顶点i指向的点
            if(!(--index[v]))//所有i指向的点的入度都减一
            {
                S.Push(v);//减完之后入度为0的说明又可以作为下一个入栈的点
            }
            
        }//for
    }//while
    if(count<G.vexnum)//手动画一遍即可知如果有环,会在遍历完所有结点前,栈就空了,因此会提前跳出while循环而导致count值小于G.vexnum
    {
        return false;//有环
    }
    else
        return true;
}

这是邻接表存储的,时间复杂度为O(|V|+|E|);

如果采用的是邻接矩阵存储则为O(|V|²);

3.逆拓扑排序

  1. 倒过来,找出度为0的,用临界矩阵或逆邻接表

  2. DFS(不咋对,别看了)

    • 不带判断环的只要在DFS的最后打印顶点就行
    • 带判断的
    bool isTree(Graph G)
    {
        for(v=0;v<G.vexnum;v++)
            visited[v]=false;
        int c=0;
        int flag=0;
        for(v=0;v<G.vexnum;v++)
        {
            if(!visited[v])
            {
                if(c=1)//说明调用过一次了,再调用就是非连通的,不是一棵树
                    return false;
                else
                {
                    DFS(G,v,&flag);
                    c++;
                }
            }
        }
        if(flag==1) return false;
        return true;
    }
    void DFS(Graph G,int v,int &flag)
    {
        //visit(v);
        visited[v]=true;
        for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
        {
            if(visited[w])//访问过,说明有环
            {
                flag=1;
            }
            else
            {
                DFS(G,w,flag);
            }
        }
        print(v);
    }
    
0CkpHP.png

7 关键路径

1 AOE网

wvft0J.png
wvfBp6.png