图基本概念与常用算法

一、思维导图

图基本概念与常用算法

二、重要概念

1.图的定义和基本术语

a.图的结构定义

定义:是由一个顶点集 V 和一个顶点间的关系集合组成的数据结构Graph = (V , VR);

b.基本术语

·有(无)向网

·子图

·完全图

·有向完全图

·稀疏图、稠密图

·邻接点、相关联、度

·路径、路径长度

·连通图、连通分量、强连通图、强连通分量

·生成树、生成森林

2.图的存储结构

a.邻接矩阵

#define MaxInt 32767 
#define MVNum 100 
typedef char VerTexType; 
typedef int ArcType; 
typedef struct { 
VerTexType vexs[MVNum]; // 顶点表
ArcType arcs[MVNum][MVNum]; // 弧的信息(邻接矩阵)
int vexnum, arcnum; // 图的顶点数和边数
} AMGraph; //邻接矩阵

b.邻接表

表(弧/边):
typedef struct ArcNode { 
int adjvex; // 该弧所指向的顶点的位置
struct ArcNode *nextarc;//指向下一条弧的指针
InfoType *info; // 该弧相关信息的指针
} ArcNode;
头结点:
typedef struct VNode { 
VerTexType data; // 顶点信息
ArcNode *firstarc; // 指向第一条依附该顶点的弧
} VNode, AdjList[MVNUM];

3.图的遍历

a.深度优先遍历

1)连通图的深度优先遍历描述:

void DFS(Graph G, int v) {
//从顶点v出发,深度优先搜索遍历连通图G
访问v; visited[v] = TRUE; 
//FirstAdjVex为获得v的第一个邻接点, NextAdjVe为获得下一
个邻接点
for(w=FirstAdjVex(G, v); w>=0; w=NextAdjVex(G,v,w)) 
if (!visited[w]) DFS(G, w); 
// 对v的尚未访问的邻接顶点w递归调用DFS
}

·邻接表:按顺序访问指针,且按顺序访问其指向,并访问其指向的指向

图基本概念与常用算法

以上图为例:先访问A,A的第一个指向为B,便访问B,B的第一个指向为C,就访问C,以此类推。

·邻接矩阵:与上面的原理类似,行遍历,找第一个不为无穷的点,并遍历该点为行下标的行,循环以上步骤。

2)非连通图

先按连通图遍历步骤遍历,遍历后找原有点中未遍历过的,重复连通图遍历步骤。

深度优先遍历图的实质:对每个顶点查找其邻接点的过程

b.广度优先遍历

算法描述:

void BFSTraverse(Graph G){
·   for (v=0; v<G.vexnum; ++v)
    visited[v] = FALSE; //初始化访问标志
    InitQueue(Q); // 置空的辅助队列Q
    for ( v=0; v<G.vexnum; ++v )
        if ( !visited[v]) {// v 尚未访问
        访问v; visited[v] = TRUE; // 访问v
        EnQueue(Q, v); // v入队列
        while (!QueueEmpty(Q)) {
            DeQueue(Q, u); // 队头元素出队并置为u
            for(w=FirstAdjVex(G, u);
            w!=0;
            w=NextAdjVex(G,u,w))
            if ( ! visited[w]) {
                访问v; visited[w]=TRUE; 
                EnQueue(Q, w); // 访问的顶点w入队列
            } // if
        }//while
    } //for
}

广度优先遍历:类似于树的层次遍历,所以是先输出每个点所有的邻接点,再输出邻接点的邻接点。

4.最小生成树

a.普里姆算法:

算法描述:

struct {
VertexType adjvex; //U集中的顶点序号
VRType lowcost; //边的权值
} closedge[MAXVNUM];
void MiniSpanTree_P(MGraph G, VertexType u) {
//用普里姆算法从顶点u出发构造网G的最小生成树
    k = LocateVex ( G, u ); 
    for (j=0; j<G.vexnum; ++j )//辅助数组初始化
        if (j!=k) 
        closedge[j] = {u,G.arcs[k][j].adj }; 
        closedge[k].lowcost = 0; //初始,U={u}
    for (i=0; i<G.vexnum; ++i) {
继续向生成树上添加顶点; }
    k = Min(closedge);//求出加入生成树的下一个顶点k
    printf(closedge[k].adjvex, G.vexs[k]); 
    //输出生成树上一条边
    closedge[k].lowcost = 0;//第k顶点并入U集
    for(j = 0; j < G.vexnum; ++j) 
    //修改其它顶点的最小边
    if (G.arcs[k][j].adj < closedge[j].lowcost)
    closedge[j]={G.vexs[k], G.arcs[k][j].adj };

b.克鲁斯卡尔算法:

算法描述:

构造非连通图 ST=( V,{ } );
    k = i = 0; // k 计选中的边数
    while (k<n-1) {
        ++i;
    检查边集 E 中第 i 条权值最小的边(u,v);
    若(u,v)加入ST后不使ST中产生回路,则输出边(u,v); 
    且k++;
    }

两种算法比较:

·普里姆算法:O(n*n),适用于稠密图

·克鲁斯卡尔算法:O(eloge),适用于稀疏图

5.最短路径

a.Dijkstra算法思想(与普里姆算法类似)

算法描述:

void ShortestPath_DIJ(AMGraph G, int v0){
//初始化
    n = G.vexnum;
    for(v = 0; v<n; ++v){
        S[v] = false; //s初始为空集
        D[v] = G.arcs[v0][v]; //v0到各个顶点的弧的权值
        if (D[v] < MaxInt) 
        Path[v] = v0; //v0和v之间有弧
        else Path[v] = -1; //v0和v之间无弧
    }
    S[v0] = true; //v0加入s
    D[v0] = 0;
    for(i = 1;i < n; ++i){
    min = MaxInt;
    for(w = 0; w < n; ++w)
    if(!S[w] && D[w] < min)
        {v = w; min = D[w];}//选择一条当前最短路径
    S[v] = true; //将v加入s
    for(w = 0; w < n; ++w)//加入v后,v0到其他节点需更新
    if(!S[w] && (D[v] + G.arcs[v][w] < D[w]){
        D[w] = D[v] + G.arcs[v][w];
        Path[w] = v;
        }
    }
}

b.弗洛伊德(Floyd)算法:每一对顶点之间的最短路径

对每个顶点进行Dijkstra算法

6.有向无环图及其应用

a.有向无环图

一个无环的有向图称作有向无环图(Directed Acycline Graph),简称DAG图。

有向无环图也是描述一项工程或系统的进行过程的有效工具。

绝大多数的工程都可分为若干个活动的子工程,而这些子工程之间,通常都受着一定条件的约束。

对应于有向图,即为进行拓扑排序和求关键路径的操作。

b.拓扑排序

算法描述:

while (v<>0) {
    printf(v); ++m;
    w:=FirstAdj(v);
    while (w<>0) {
    inDegree[w]--;
    w:=nextAdj(v,w);
    }
    取下一个入度为零的顶点v;
}
if m<n printf(“图中有回路”);
CountInDegree(G,indegree); //对各顶点求入度
InitStack(S);
for ( i=0; i<G.vexnum; ++i)
if (!indegree[i]) Push(S, i); //入度为零的顶点入栈
count=0; //对输出顶点计数
while (!EmptyStack(S)) {
    Pop(S, v); ++count; printf(v);
    for (w=FirstAdj(v); w; w=NextAdj(G,v,w)){
        --indegree(w); // 弧头顶点的入度减一
        if (!indegree[w]) Push(S, w);
    }
}
if (count<G.vexnum) printf(“图中有回路”);

1)从有向图中选取一个没有前驱的顶点,并输出之;

2)从有向图中删去此顶点以及所有以它为尾的弧;

3)重复上述两步,直至图空,或者图不空但找不到无

前驱的顶点为止。

4)没有前驱的顶点  入度为零的顶点;

删除顶点及以它为尾的弧  弧头顶点的入度减1。

7.关键路径

·整个工程完成的时间为:从有向图的源点到汇点的最长路径

·“关键活动”指的是:该弧上的权值增加将使有向图上的最长路径的长度增加。

·事件vi的最早发生时间:从开始点v1到vi的最长路径长度。用ve(i)表示。

·事件vi的最迟发生时间:在不推迟整个工期的前提下,事件vi允许的最晚发生时间。用vl(i)表示。

·活动ai的最早开始时间:即为ai的弧尾顶点(事件)的最早发生时间。用e(i)表示。

·活动ai的最迟开始时间:在保证不推迟整个工程的完成时间的前提下,活动ai的最迟开始时间。用l(i)表示。

·计算公式:e(i) = ve(j)(j为边的头)

l(i) =vl(k) - dut(<j, k>)(k为边的尾,dut为边的权重)

·求ve的顺序应该是按拓扑有序的次序,求vl的顺序应该是按拓扑逆序的次序。

·根据e(i)、l(i)能更准确求出关键路径。

三、疑难问题

1.Dijkstra算法思想中的Path[]数组

每点对应的值是该点所对应最短路径的上一个顶点

图基本概念与常用算法

以上图为例

要到达的顶点为0,按顺序分别是2,3,4,5的数组值:

所以2到1的最短路径为2到1,

3到1的为3到4到1,

4到1的为4到1,

5到1的为5到3到4到1.

原文链接: https://www.cnblogs.com/dornawe/p/12906762.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    图基本概念与常用算法

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/349131

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午5:31
下一篇 2023年3月2日 上午5:32

相关推荐