目录

  • 最小生成树
    • 普里姆(Prim)算法
      • 算法实现
      • 运行结果
      • 程序分析
    • 克鲁斯卡尔(Kruskal)算法
      • 算法实现
      • 运行结果
      • 程序分析
  • 最短路径
    • 广度优先搜索(BFS)算法
      • 算法实现
      • 运行结果
      • 程序分析
    • 狄克斯特拉(Dijkstra)算法
      • 算法实现
      • 运行结果
      • 程序分析
    • 弗洛伊德(Floyd)算法
      • 算法实现
      • 运行结果
      • 程序分析
    • 总结

数据结构-图(第八章)的整理笔记,若有错误,欢迎指正。

  • 图的应用

图的应用主要包括:最小生成(代价)树、最短路径、拓扑排序和关键路径。

最小生成树

  • 一个连通图的生成树包含图的所有顶点,并且只含尽可能少的边。对于生成树来说,若砍去它的一条边,则会使生成树变成非连通图;若给它增加一条边,则会形成图中的一条回路。
  • 对于一个带权连通无向图G=(V,E),生成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同。
  • 设R为G的所有生成树的集合,若T为R中边的权值之和最小的那棵生成树,则T称为G的最小生成树(Minimum- Spanning-Tree,MST)。
  • 不难看出,最小生成树具有如下性质:
  1. 最小生成树不是唯一的,即最小生成树的树形不唯一,R中可能有多个最小生成树。当图G中的各边权值互不相等时,G的最小生成树是唯一的;若无向连通图G的边数比顶点数少1,即G本身是一棵树时,则G的最小生成树就是它本身。

    !注意: 只有连通图才有生成树,非连通图只有生成森林。
  2. 最小生成树的边的权值之和总是唯一的,虽然最小生成树不唯一,但其对应的边的权值之和总是唯一的,而且是最小的。
  3. 最小生成树的边数为顶点数减1。
  • 构造最小生成树有多种算法,但大多数算法都利用了最小生成树的下列性质:假设G=(V,E)是一个带权连通无向图,U是顶点集的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。
  • 基于该性质的最小生成树算法主要有Prim算法Kruskal算法,它们都基于贪心算法的策略。

普里姆(Prim)算法

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

算法实现

  • 由于普里姆(Prim)算法中需要频繁地取一条条边的权,所以图采用邻接矩阵更合适。
  • Pim算法中的候选边是指集合U和V-U之间的所有边(称为U和V-U两个顶点集合的割集),如果把这些边都保存起来是非常消耗空间的,实际上考虑候选边的目的是求U和V-U之间的最小边(指权最小的边)。为此只考虑V-U集合的顶点(因为两个顶点集之间的边是无向边),建立两个数组closest和lowcost,用于记录V-U中顶点i到U中顶点的最小边。
  • 对于V-U中的一个顶点j,它的最小边对应U的某个顶点,用 closest[j]保存U中的这个顶点,顶点j的最小边对应U中的顶点k,有closest[j]=k,并且用lowcost[j]存储该最小边的权。也就是说,这样的最小边为(closest[j],j)边,对应的权为lowcost[j]。
  • 这里的约定是若某个顶点i有lowcost[i]=0,表示i∈U;若0<lowcost[i]<∞(或者lowcost[i]≠0),表示i∈V-U。
  • 初始时,U中只有一个顶点v。对于所有顶点i,这时(v,i)边就是顶点i到U的最小边,置lowcost[i]=g.edges[v][i](没有边时为∞,v到v为0),closest[i]=v。由于lowcost[v]已经被置为0,表示它添加到U集合中了。
  • 在候选边中求ー条最小边的过程是扫描V-U中的所有顶点j,通过比较lowcost值求出最小lowcost值对应的顶点k,那么(closest[k],k)就是最小边,输出这条最小边,并将顶点k添加到U中,即置 lowcost[k]=0。
  • 接着做调整,也就是修改侯选边,也仅仅考虑V-U集合的顶点。对于j∈V-U(即lowcost[j]!=0),在上一步(顶点k还没有添加到U中时),lowcost[j]保存的是顶点j到U中顶点closest[j]的最小边,而现在U发生了改変(改变是仅仅在U中增加了顶点k),所以需要将原来的lowcost[j]与g.edge[k][i]进行比较,如果g.edge[k][i]小,选择(k,j)作为新的最小边,即置lowcost[j]=g.edge[k][i],closest[j]=k,否则顶点j的候选边不改变。
void Prim(MGraph g, int v)
{int mincost = 0;int lowcost[MaxVertenNum];int MIN;int closest[MaxVertenNum], i, j, k;for (i = 0; i < g.vexnum; i++) //给lowcost[]和closest[]置初值{lowcost[i] = g.Edge[v][i];closest[i] = v;}for (i = 1; i < g.vexnum; i++) //找出(n-1)个顶点{MIN = INF;for (j = 0; j < g.vexnum; j++) //在(V-U)中找出离U最近的顶点kif (lowcost[j] != 0 && lowcost[j] < MIN){MIN = lowcost[j]; //k记录最近顶点的编号k = j;}printf("边(%c, %c)权为:%d\n", g.Vex[closest[k]], g.Vex[k],MIN); //输出最小生成树的一条边mincost = mincost + MIN;lowcost[k] = 0; //标记k已经加入Ufor (j = 0; j < g.vexnum; j++) //对(V-U)中的顶点j进行调整if (lowcost[j] != 0 && g.Edge[k][j] < lowcost[j]){lowcost[j] = g.Edge[k][j];closest[j] = k; //修改数组lowcost和closest}}printf("最小代价为:%d\n", mincost);
}

运行结果


程序分析

  • Prim()算法中有两重for循环,所以时间复杂度为O(∣V∣2)O(|V|^2)O(∣V∣2),其中|V|为图的顶点个数。可以看出,Prim()算法的执行时间与图中的边数e无关,所以它特别适合用稠密图求最小生成树。

克鲁斯卡尔(Kruskal)算法

  • 每次选择⼀条权值最小的边,使这条边的两头连通(原本已经连通的就不选),直到所有结点都连通。
  • 结果同Prim算法。

算法实现

  • 和普里姆(Prim)算法一样,在该算法中需要频繁地取一条条边的权,所以图采用邻接矩阵更合适。
  • 设置一个辅助数组vset[0…n-1],vset[i]用于记录一个顶点所在的连通分量编号。初值时每个顶点构成一个连通分量,所以有 vset[i]=i,vset[j]=j(所有顶点的连通分量编号等于该顶点编号)。当选中(i,j)边时,如果顶点j的连通分量编号相同,表示加入后会出现回路,不能加入;否则表示加入后不会出现回路,可以加入,然后将这两个顶点所在连通分量中所有顶点的连通分量编号改为相同(改为vset[i]或者vset[j]均可)。
  • 另外,用一个数组E[ ]存放图G中的所有边,要求它们是按权值从小到大的顺序排列的,为此先从图G的邻接矩阵中获取所有边集E,再采用直接插入排序法对边集E按权值递增排序。
void Kruskal(MGraph g) //Kruskal算法
{int i, j, ul, vl, sn1, sn2, k;int vset[MaxVertenNum];Edge E[MaxVertenNum]; //存放图中的所有边k = 0; //e数组的下标从0开始计数for (i = 0; i < g.vexnum; i++) //由g产生边集E,不重复选取同一条边for (j = 0; j <= i; j++)if (g.Edge[i][j] != 0 && g.Edge[i][j] != INF){E[k].u = i;E[k].v = j;E[k].w = g.Edge[i][j];k++;}Insertsort(E, g.Edge); //采用直接插入排序对E数组按权值递增排序for (i = 0; i < g.vexnum; i++) vset[i] = i; //初始化辅助数组k = 1; //k表示当前构造生成树的第几条边,初值为1j = 0; //E中边的下标, 初值为0while (k < g.vexnum) //生成的边数小于顶点数时循环{ul = E[j].u;vl = E[j].v; //取一条边的两个顶点sn1 = vset[ul];sn2 = vset[vl]; //分别得到两个顶点所属的集合编号if (sn1 != sn2) //两顶点属于不同的集合, 该边是最小生成树的一条边{printf("(%d, %d):%d\n", ul, vl, E[j].w); //输出最小生成树的一条边k++; //生成边数增1for (i = 0; i < g.vexnum; i++) //两个集合统一编号if (vset[i] == sn2) //集合编号为sn2的改为sn1vset[i] = sn1;}j++; //扫描下一条边}
}

运行结果

程序分析

  • 时间复杂度:O(∣E∣log2∣E∣)O(|E|log_2^{|E|})O(∣E∣log2∣E∣​) ,可以看出,Kruskal算法的执行时间仅与图中的边数有关,与顶点数无关,所以它特别适合用边稀疏图求最小生成树。

最短路径

  • 广度优先搜索查找最短路径只是对无权图而言的。当图是带权图时,把从一个顶点v0v_0v0​到图中其余任意一个顶点viv_ivi​的一条路径(可能不止一条)所经过边上的权值之和,定义为该路径的带权路径长度,把带权路径长度最短的那条路径称为最短路径
  • 求解最短路径的算法通常都依赖于一种性质,即两点之间的最短路径也包含了路径上其他顶点间的最短路径。带权有向图G的最短路径问题一般可分为两类:一是单源最短路径,即求图中某一顶点到其他各顶点的最短路径;二是求每对顶点间的最短路径。
    最短路径问题{单源最短路径{广度优先搜索(BFS)算法(无权图)狄克斯特拉(Dijkstra)算法(带权图、无权图)各顶点间的最短路径—弗洛伊德(Floyd)算法(带权图、无权图)最短路径问题\begin{cases} 单源最短路径 \begin{cases} 广度优先搜索(BFS)算法(无权图)\\ 狄克斯特拉(Dijkstra)算法(带权图、无权图) \end{cases}\\ 各顶点间的最短路径 —弗洛伊德(Floyd)算法(带权图、无权图) \end{cases} 最短路径问题⎩⎪⎨⎪⎧​单源最短路径{广度优先搜索(BFS)算法(无权图)狄克斯特拉(Dijkstra)算法(带权图、无权图)​各顶点间的最短路径—弗洛伊德(Floyd)算法(带权图、无权图)​
  • 最短路径一定是简单路径。

广度优先搜索(BFS)算法

  • 无权图可以视为一种特殊的带权图,只是每条边的权值都为1。

算法实现

void BFS_Min_Distance(MGraph g,int v) //广度优先遍历求不带权无向图最短路径
{bool visited[MaxVertenNum];int d[MaxVertenNum], path[MaxVertenNum];for (int i = 0; i < g.vexnum; i++) //初始化数组{visited[i] = false; //初始时全部为falsed[i] = INF; //初始时全部为∞path[i] = -1; //初始时全部为-1}SqQueue* qu;InitQueue(qu); //初始化队列visited[v] = 1; //置访问标记d[v] = 0; //顶点v到顶点v的最短路径为0enQueue(qu, v);while (!EmptyQueue(qu)){deQueue(qu, v);for (int w = FirstNeighbor(g, v); w >= 0; w = NextNeighbor(g, v, w))if (!visited[w]) //尚未访问的邻接顶点{d[w] = d[v] + 1; //前驱顶点的路径值+1path[w] = v; //记录前驱顶点序号visited[w] = true; //置访问标记enQueue(qu, w); }}Destroy(qu);
}

运行结果

  • 不带权无向图(邻接矩阵法)
  • 不带权无向图(邻接表法)
  • 不带权有向图(邻接矩阵法)
  • 不带权有向图(邻接表法)

程序分析

  • 在BFS算法的基础之上进行改进,增加了两个数组d[ ]和path[ ],d[ ]用来记录最短路径值,path[ ]用来记录当前顶点的前驱顶点序号。path[ ]为-1时,表示是从该顶点开始进行的遍历。
  • 当要求起始遍历顶点到其余每个顶点的最短路径时,访问数组d[ ]即可,按照d[ ]中的值,能够在path[ ]中找到一条最短路径。
  • BFS算法求单源最短路径只适用于无权图,或所有边的权值都相同的图。

狄克斯特拉(Dijkstra)算法

  • Dijkstra算法设置一个集合S记录已求得的最短路径的顶点,初始时把源点v放入S,集合S每并入一个新顶点,都要修改源点到集合V-S中顶点当前的最短路径长度值。
  • 在构造的过程中还设置了两个辅助数组:
  1. dist[ ]:记录从源点v到其他各顶点当前的最短路径长度,它的初态为:若从v到有弧,则dist[i]为弧上的权值;否则置dist[i]为∞。
  2. path[ ]:path[i]表示从源点到顶点i之间的最短路径的前驱结点。在算法结束时,可根据其值追溯得到源点到顶点的最短路径。
  • Dijkstra算法的步骤如下(不考虑对path[ ]的操作):
  1. 初始化:集合S初始为{0},dist[]的初始值dist[i]=arcs[0][i],i=1,2,…,n-1。
  2. 从顶点集合V-S中选出vjv_jvj​,满足dist[j]= Min{dist[i] | viv_ivi​∈V-S},vjv_jvj​就是当前求得的一条从v0v_0v0​出发的最短路径的终点,令S=S∪{j}。
  3. 修改从v0v_0v0​出发到集合V-S上任一顶点vkv_kvk​可达的最短路径长度:若dist[j]+arcs[j][k]<dist[k],则更新dist[k]=dist[j]+arcs[j][k]。
  4. 重复2)~3)操作共n-1次,直到所有的顶点都包含在S中。


算法实现

void Dispath(MGraph g, int dist[], int path[], int S[], int v)
//输出从顶点v出发的所有最短路径
{int i, j, k;int apath[MaxVertenNum], d;for (i = 0; i < g.vexnum; i++)if (S[i] == 1 && i != v){printf("从顶点%c到顶点%c的最短路径长度为:%d\t路径为:", g.Vex[v], g.Vex[i], dist[i]);d = 0;apath[d] = i;k = path[i];if (k == -1) printf("无路径\n"); //没有路径的情况else //存在路径时输出该路径{while (k != v){d++;apath[d] = k;k = path[k];}d++;apath[d] = v; //添加路径上的起点printf("%c", g.Vex[apath[d]]); //先输出起点for (j = d - 1; j >= 0; j--) printf(",%c", g.Vex[apath[j]]); //再输出其他顶点printf("\n");}}
}void Dijksra(MGraph g, int v)
{int dist[MaxVertenNum], path[MaxVertenNum];int S[MaxVertenNum]; //S[i]=1表示顶点i在S中,S[i]=0表示顶点i在U中int MINdis, i, j, u;for (i = 0; i < g.vexnum; i++){dist[i] = g.Edge[v][i]; //距离初始化S[i] = 0;if (g.Edge[v][i] < INF) //路径初始化path[i] = v; //顶点v到顶点i有边时,置顶点i的前一个顶点为velse path[i] = -1; //顶点v到顶点i没边时,置顶点i的前一个顶点为-1}S[v] = 1; //源点编号v放入S中path[v] = 0;for (i = 0; i < g.vexnum - 1; i++) //循环直到所有顶点的最短路径都求出{MINdis = INF; //MINdis置最大长度初值for(j=0;j<g.vexnum;j++)if (S[j] == 0 && dist[j] < MINdis){u = j;MINdis = dist[j];}S[u] = 1; //顶点u加入S中for (j = 0; j < g.vexnum; j++) //修改不在S中(即U中)的顶点的最短路径if(S[j]==0)if (g.Edge[u][j] < INF && dist[u] + g.Edge[u][j] < dist[j]){dist[j] = dist[u] + g.Edge[u][j];path[j] = u;}}Dispath(g, dist, path, S, v); //输出最短路径
}

运行结果

程序分析

  • 显然,狄克斯特拉(Dijkstra)算法也是基于贪心策略的。使用邻接矩阵表示时,时间复杂度为O(∣V∣2)O(|V|^2)O(∣V∣2)。使用带权的邻接表表示时,虽然修改dist[ ]的时间可以减少,但由于在dist[ ]中选择最小分量的时间不变,时间复杂度仍为O(∣V∣2)O(|V|^2)O(∣V∣2)。
  • 狄克斯特拉(Dijkstra)算法和普里姆(Prim)算法的相似之处:前者的dist[ ]数组记录的是从当前顶点到达某一个指定顶点的最短路径的值;后者的lowCost[ ]数组记录的是这些顶点加入到目前组建的生成树的最小代价。二者算法的时间复杂度相同。
  • !注意:Dijkstra算法不适用于有负权值的带权图。

弗洛伊德(Floyd)算法

  • 求所有顶点之间的最短路径问题描述如下:已知一个各边权值均大于0的带权有向图,对任意两个顶点vi≠vjv_i≠v_jvi​​=vj​,要求求出viv_ivi​与vjv_jvj​之间的最短路径和最短路径长度。
  • Floyd算法的基本思想是:递推产生一个n阶方阵序列A(−1),A0,…,Ak,…,An−1A^{(-1)},A^{0},…,A^{k},…,A^{n-1}A(−1),A0,…,Ak,…,An−1,其中A(k)[i][j]A^{(k)}[i][j]A(k)[i][j]表示从顶点viv_ivi​到顶点vjv_jvj​的路径长度,k表示绕行第k个顶点的运算步骤。
  • 初始时,对于任意两个顶点viv_ivi​和vjv_jvj​,若它们之间存在边,则以此边上的权值作为它们之间的最短路径长度;若它们之间不存在有向边,则以∞作为它们之间的最短路径长度。以后逐步尝试在原路径中加入顶点k(k=0,1,…,n-1)作为中间顶点。若增加中间顶点后,得到的路径比原来的路径长度减少了,则以此新路径代替原路径。
  • 算法描述如下:
    定义一个n阶方阵序列A(−1),A0,…,An−1A^{(-1)},A^{0},…,A^{n-1}A(−1),A0,…,An−1,其中,A(−1)[i][j]=arcs[i][j]A^{(-1)}[i][j]=arcs[i][j]A(−1)[i][j]=arcs[i][j],A(k)[i][j]=MinA(k−1)[i][j],A(k−1)[i][k]+A(k−1)[k][j],k=0,1,...,n−1A^{(k)}[i][j]=Min{A^{(k-1)}[i][j]},A^{(k-1)}[i][k]+A^{(k-1)}[k][j],k=0,1,...,n-1A(k)[i][j]=MinA(k−1)[i][j],A(k−1)[i][k]+A(k−1)[k][j],k=0,1,...,n−1
    式中,A(0)[i][j]A^{(0)}[i][j]A(0)[i][j]是从顶点viv_ivi​到vjv_jvj​、中间顶点是v0v_0v0​的最短路径的长度,A(k)[i][j]A^{(k)}[i][j]A(k)[i][j]是从顶点viv_ivi​到vjv_jvj​、中间顶点的序号不大于k的最短路径的长度。
  • Floyd算法是一个迭代的过程,每迭代一次,在从viv_ivi​到vjv_jvj​到的最短路径上就多考虑了一个顶点;经过n次迭代后,所得到的A(n−1)[i][j]A^{(n-1)}[i][j]A(n−1)[i][j]就是viv_ivi​到vjv_jvj​的最短路径长度,即方阵A(n−1)A^{(n-1)}A(n−1)中就保存了任意一对项点之间的最短路径长度。

算法实现

void Dispath(MGraph g, int A[][MaxVertenNum], int path[][MaxVertenNum])
{int i, j, k, s;int apath[MaxVertenNum], d; //存放一条最短路径中间顶点(反向)及其顶点个数for (i = 0; i < g.vexnum; i++)for (j = 0; j < g.vexnum; j++){if (A[i][j] != INF && i != j) //若顶点i和j之间存在路径{printf("从%c到%c的路径为:", g.Vex[i], g.Vex[j]);k = path[i][j];d = 0;apath[d] = j; //路径上添加终点while (k != -1 && k != i) //路径上添加中间点{d++;apath[d] = k;k = path[i][k];}d++;apath[d] = i; //路径上添加起点printf("%c", g.Vex[apath[d]]); //输出起点for (s = d - 1; s >= 0; s--) printf("_%c", g.Vex[apath[s]]); //输出路径上的中间顶点printf("\t路径长度为:%d\n", A[i][j]);}}
}void Floyd(MGraph g)
{int A[MaxVertenNum][MaxVertenNum], path[MaxVertenNum][MaxVertenNum];int i, j, k;for(i=0;i<g.vexnum;i++)for (j = 0; j < g.vexnum; j++){A[i][j] = g.Edge[i][j]; //A[]数组中的值等于邻接矩阵表中的值if (i != j && g.Edge[i][j] < INF) path[i][j] = i;else path[i][j] = -1;//path[i][j] = -1; //初始时,path[]数组值全为-1}for (k = 0; k < g.vexnum; k++) //遍历整个矩阵,i为行号,j为列号for (i = 0; i < g.vexnum; i++)for (j = 0; j < g.vexnum; j++)if (A[i][j] > A[i][k] + A[k][j]){A[i][j] = A[i][k] + A[k][j]; //更新最短路径长度path[i][j] = path[k][j]; //中转点}Dispath(g, A, path);
}

运行结果


程序分析

  • Floyd算法的时间复杂度为O(∣V∣3)O(|V|^3)O(∣V∣3)。不过由于其代码很紧湊,且并不包含其他复杂的数据结构,因此隐含的常数系数是很小的,即使对于中等规模的输入来说,它仍然是相当有效的。
  • Floyd算法允许图中有带负权值的边,但不允许有包含带负权值的边组成的回路。 Floyd算法同样适用于带权无向图,因为带权无向图可视为权值相同往返二重边的有向图。
  • 也可以用单源最短路径算法来解决每对顶点之间的最短路径问题。轮流将每个顶点作为源点,并且在所有边权值均非负时,运行一次Dijkstra算法,其时间复杂度为O(∣V∣2⋅∣V∣)=O(∣V∣3)O(|V|^2\cdot|V|)=O(|V|^3)O(∣V∣2⋅∣V∣)=O(∣V∣3)。

总结

广度优先搜索(BFS)算法 狄克斯特拉(Dijkstra)算法 弗洛伊德(Floyd)算法
⽆权图
带权图 ×
带负权值的图 × ×
带负权回路的图 × × ×
时间复杂度 O(|V|2^22)或O(|V|+|E|) O(|V|2^22) O(|V|3^33)
通常用于 求无权图的单源最短路径 求带权图的单源最短路径 求带权图中各顶点间的最短路径
  • 也可用Dijkstra算法求所有顶点间的最短路径,重复|V|次即可,总的时间复杂度也是O(∣V∣3)O(|V|^3)O(∣V∣3)。

数据结构—图(Part Ⅱ)—最小生成树 最短路径相关推荐

  1. 数据结构之图(最小生成树+最短路径)

    基本概念 连通:若a->b存在路径,即为连通 连通图:该图中任意两点均连通,即为连通图 连通分量:下图为无向图,但存在三个连通分量 强连通图:双向的连通图 强连通分量:有向图中的双向连通 图的存 ...

  2. 数据结构——图的定义和实现

    这里写目录标题 图的定义 各种图的定义 无向边 有向边 图的顶点与边的关系 连通图生成树 图的各种实现 在引入邻接矩阵之前先介绍一下图的相关概念(概念比前树啥的面稍微复杂一点) 图的定义 之前学了线性 ...

  3. 图的最小生成树和最短路径算法思路总结(Prim,Kruskal,Dijkstra,Floyd)

    带权无向图->最小生成树算法->Prim算法: 思路: 首先,我们先设置两个集合,U_{}:一个用来放最小生成树的顶点,T_{}:一个用来放最小生成树的边.选取最开始的点V_0,将V_0放 ...

  4. 数据结构(19)图的最小生成树算法

    数据结构(19)图的最小生成树算法 前言 普里姆(Prim)算法 克鲁斯卡尔(Kruskal)算法 代码 GraphMtx.h GraphMtx.c Main.c 前言 在有n个顶点的图中,要连接所有 ...

  5. 数据结构—图及其应用(交通问题,实现最短路径、最短时间、最少费用查询)

    数据结构-图及其应用(交通问题,实现最短路径.最短时间.最少费用查询) 1.任务描述 (1).任务: 设计一个城市交通咨询模拟系统,利用该系统实现至少两种最优决策:最短路程到达.最省时到达等线路规划. ...

  6. 数据结构 图定义和实现查询最短路径

    该题目为郑州轻工业大学数据结构实验四的一道算法实验题,仅分享LZ的思路,代码提供参考 根据郑州市方特欢乐主题公园设计一个简单的游玩路线,设计数据结构和算法实现相应功能.要求所含景点不少于8个(方特城堡 ...

  7. 【数据结构】——图的最小生成树算法(普里姆+克鲁斯卡尔)

    这里的图指的是带权无向图,也就是无向网. 关于最小生成树 图的最小生成树要解决的问题:用最小的代价连通图中的所有顶点. 下面两种算法都是运用贪心思想,利用MST(Minimum Spanning Tr ...

  8. 数据结构 图 简单入门 最小生成树 深度优先搜索 广度优先搜索

    早上上班路上,看到一则新闻,广州的.新闻说的是一个辅警在路边查过往路人的身份证,其中有一个记者,就询问,根据哪条法律要查询他的身份证.辅警当然不知道(事后据说,就是闲着无聊,查着玩的!),也肯定不会认 ...

  9. 【数据结构-图】2.多图详解最小生成树(多图详解+实现代码)

    最小生成树: 这个定义有两个约束:最小和树 对于树,从而引出以下三个最小生成树的特点 在图中无环 连接所有图中的点 N个顶点,有N-1条边 最小:指的是生成这棵树的边的权值之和最小 最小生成树的求取有 ...

最新文章

  1. 获取图片像素颜色,转换为CSS3 box-shadow显示
  2. Linux服务器集群系统(一)—— LVS(Linux Virtual Server)简介
  3. 数据库查询新增一列默认值
  4. 热式气体质量流量计检定规程_宁夏热式气体质量流量计价位,玻璃管液位计怎么样...
  5. Python操作MySQL存储,这些你都会了吗?
  6. Linux逻辑运算优先级,linux中的逻辑运算和正则表达式
  7. big sur 黑苹果_苹果宣布11日再开发布会!自研芯片届时或将发布!
  8. vfp 界面_VFP之老树新花
  9. mysql——数据库设计中int与varchar中的长度含义
  10. linux网络测速qerf,cywapp.net
  11. Python新闻网站项目-4.数据处理和算法应用
  12. win10计算机桌面路径,win10桌面路径是什么?如何修改win10桌面文件路径?
  13. 桌面窗口管理器和csrss导致Windows的GPU和内存占用过高而卡顿
  14. 20+非常棒的Photoshop卡通设计教程
  15. 红帽linux如何装软件,redhat 下软件的安装
  16. 系统测试总结报告模板
  17. 2020下半年软考中级(系统集成项目管理工程师)(个人备考用)
  18. iOS开发支付集成之支付宝支付
  19. Java方法excel文件转换成xml文件
  20. 利用unicode字符rlo

热门文章

  1. 【哈利波特】Sherbert Lemon对HP的解读之五
  2. ubnutu下载网易云音乐
  3. java 文件去除扩展名_在Java中删除文件扩展名
  4. emmagee工具怎么玩转app压力测试监控
  5. t6文件服务器怎么设置,t6文件服务器设置
  6. php5.3 发送邮件phpemail的使用 (适用php5.3)
  7. 电脑数据迁移到新电脑:一键迁移数据方案详解
  8. 如何在家远程控制公司的电脑,高效便捷的远程办公?
  9. Vue 计算时间差,几分钟之前、几小时前、几天之前、几个月前
  10. 计算机毕业设计springboot酒店客房管理系统8yj0v源码+系统+程序+lw文档+部署