图的基本算法(最小生成树)
2018-02-18 16:21
330 查看
假设以下情景,有一块木板。板上钉上了一些钉子。这些钉子能够由一些细绳连接起来。假设每一个钉子能够通过一根或者多根细绳连接起来。那么一定存在这种情况,即用最少的细绳把全部钉子连接起来。
更为实际的情景是这种情况。在某地分布着
以上这些问题都能够归纳为最小生成树问题,用正式的表述方法描写叙述为:给定一个无方向的带权图
解决最小生成树问题已经有前人开道,
算法描写叙述:
1. 在一个加权连通图中,顶点集合
2. 随意选出一个点作为初始顶点,标记为
3. 反复以下操作,直到全部点都被标记为
在剩下的点钟。计算与已标记
以下我们来看一个最小生成树生成的过程:
1 起初,从顶点
2 选择顶点
3 与之相连的点距离分别为
4 计算与
5 接下来非常明显。
6
7 最后仅仅有
针对如上的图,代码实比例如以下:
以下还是用一组图示来表现算法的过程:
1 初始情况,一个联通图,定义针对边的数据结构,包括起点,终点。边长度:
2 在算法中首先取出全部的边,将边依照长短排序,然后首先取出最短的边,将
3 继续找到第二短的边,将
4 继续找。找到第三短的边
5 继续找,找到
6 再找,找到
这样全部的点都归到一个集合里,生成了最小生成树。
依据上图实现的代码例如以下:
更为实际的情景是这种情况。在某地分布着
N个村庄。如今须要在
N个村庄之间修路,每一个村庄之前的距离不同,问怎么修最短的路,将各个村庄连接起来。
以上这些问题都能够归纳为最小生成树问题,用正式的表述方法描写叙述为:给定一个无方向的带权图
G=(V, E),最小生成树为集合
T,
T是以最小代价连接
V中全部顶点所用边
E的最小集合。 集合
T中的边能够形成一颗树。这是由于每一个节点(除了根节点)都能向上找到它的一个父节点。
解决最小生成树问题已经有前人开道,
Prime算法和
Kruskal算法。分别从点和边下手攻克了该问题。
Prim算法
Prim算法是一种产生最小生成树的算法。该算法于
1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:
Vojtěch Jarník)发现。并在
1957年由美国计算机科学家罗伯特·普里姆(英语:
Robert C. Prim)独立发现。
1959年,艾兹格·迪科斯彻再次发现了该算法。
Prim算法从随意一个顶点開始,每次选择一个与当前顶点集近期的一个顶点,并将两顶点之间的边增加到树中。
Prim算法在找当前近期顶点时使用到了贪婪算法。
算法描写叙述:
1. 在一个加权连通图中,顶点集合
V。边集合为
E
2. 随意选出一个点作为初始顶点,标记为
visit,计算全部与之相连接的点的距离,选择距离最短的,标记
visit.
3. 反复以下操作,直到全部点都被标记为
visit:
在剩下的点钟。计算与已标记
visit点距离最小的点,标记
visit,证明增加了最小生成树。
以下我们来看一个最小生成树生成的过程:
1 起初,从顶点
a開始生成最小生成树
2 选择顶点
a后,顶点啊置成
visit(涂黑),计算周围与它连接的点的距离:
3 与之相连的点距离分别为
7,
6,
4,选择
C点距离最短,涂黑
C,同一时候将这条边高亮增加最小生成树:
4 计算与
a,c相连的点的距离(已经涂黑的点不计算),由于与
a相连的已经计算过了,仅仅须要计算与
c相连的点,假设一个点与
a,c都相连。那么它与
a的距离之前已经计算过了,假设它与c的距离更近。则更新距离值。这里计算的是未涂黑的点距离涂黑的点的近期距离,非常明显,
b和
a为
7,
b和
c的距离为
6。更新
b和已訪问的点集距离为
6,而
f,
e和
c的距离各自是
8,
9,所以还是涂黑
b,高亮边
bc:
5 接下来非常明显。
d距离
b最短。将
d涂黑。
bd高亮:
6
f距离
d为
7,距离
b为
4。更新它的最短距离值是
4。所以涂黑
f,高亮
bf:
7 最后仅仅有
e了:
针对如上的图,代码实比例如以下:
#include<iostream> #define INF 10000 using namespace std; const int N = 6; bool visit ; int dist = { 0, }; int graph = { {INF,7,4,INF,INF,INF}, //INF代表两点之间不可达 {7,INF,6,2,INF,4}, {4,6,INF,INF,9,8}, {INF,2,INF,INF,INF,7}, {INF,INF,9,INF,INF,1}, {INF,4,8,7,1,INF} }; int prim(int cur) { int index = cur; int sum = 0; int i = 0; int j = 0; cout << index << " "; memset(visit, false, sizeof(visit)); visit[cur] = true; for (i = 0; i < N; i++) dist[i] = graph[cur][i];//初始化。每一个与a邻接的点的距离存入dist for (i = 1; i < N; i++) { int minor = INF; for (j = 0; j < N; j++) { if (!visit[j] && dist[j] < minor) //找到未訪问的点中,距离当前最小生成树距离最小的点 { minor = dist[j]; index = j; } } visit[index] = true; cout << index << " "; sum += minor; for (j = 0; j < N; j++) { if (!visit[j] && dist[j]>graph[index][j]) //运行更新,假设点距离当前点的距离更近,就更新dist { dist[j] = graph[index][j]; } } } cout << endl; return sum; //返回最小生成树的总路径值 } int main() { cout << prim(0) << endl;//从顶点a開始 return 0; }
Kruskal算法
Kruskal是还有一个计算最小生成树的算法,其算法原理例如以下。首先,将每一个顶点放入其自身的数据集合中。然后,依照权值的升序来选择边。当选择每条边时,推断定义边的顶点是否在不同的数据集中。假设是,将此边插入最小生成树的集合中,同一时候。将集合中包括每一个顶点的联合体取出。假设不是。就移动到下一条边。反复这个过程直到全部的边都探查过。以下还是用一组图示来表现算法的过程:
1 初始情况,一个联通图,定义针对边的数据结构,包括起点,终点。边长度:
typedef struct _node{ int val; //长度 int start; //边的起点 int end; //边的终点 }Node;
2 在算法中首先取出全部的边,将边依照长短排序,然后首先取出最短的边,将
a,
e放入同一个集合里,在实现中我们使用到了并查集的概念:
3 继续找到第二短的边,将
c,
d再放入同一个集合里:
4 继续找。找到第三短的边
ab,由于
a,
e已经在一个集合里。再将
b增加:
5 继续找,找到
b,
e。由于
b,
e已经同属于一个集合,连起来的话就形成环了。所以边
be不增加最小生成树:
6 再找,找到
bc,由于
c,
d是一个集合的。
a,
b,
e是一个集合。所以再合并这两个集合:
这样全部的点都归到一个集合里,生成了最小生成树。
依据上图实现的代码例如以下:
#include<iostream> #define N 7 using namespace std; typedef struct _node{ int val; int start; int end; }Node; Node V ; int cmp(const void *a, const void *b) { return (*(Node *)a).val - (*(Node*)b).val; } int edge [3] = { { 0, 1, 3 }, { 0, 4, 1 }, { 1, 2, 5 }, { 1, 4, 4 }, { 2, 3, 2 }, { 2, 4, 6 }, { 3, 4, 7} }; int father = { 0, }; int cap = {0,}; void make_set() //初始化集合,让全部的点都各成一个集合。每一个集合都仅仅包括自己 { for (int i = 0; i < N; i++) { father[i] = i; cap[i] = 1; } } int find_set(int x) //推断一个点属于哪个集合,点假设都有着共同的祖先结点,就能够说他们属于一个集合 { if (x != father[x]) { father[x] = find_set(father[x]); } return father[x]; } void Union(int x, int y) //将x,y合并到同一个集合 { x = find_set(x); y = find_set(y); if (x == y) return; if (cap[x] < cap[y]) father[x] = find_set(y); else { if (cap[x] == cap[y]) cap[x]++; father[y] = find_set(x); } } int Kruskal(int n) { int sum = 0; make_set(); for (int i = 0; i < N; i++)//将边的顺序按从小到大取出来 { if (find_set(V[i].start) != find_set(V[i].end)) //假设改变的两个顶点还不在一个集合中,就并到一个集合里。生成树的长度加上这条边的长度 { Union(V[i].start, V[i].end); //合并两个顶点到一个集合 sum += V[i].val; } } return sum; } int main() { for (int i = 0; i < N; i++) //初始化边的数据,在实际应用中可依据详细情况转换而且读取数据,这边仅仅是測试用例 { V[i].start = edge[i][0]; V[i].end = edge[i][1]; V[i].val = edge[i][2]; } qsort(V, N, sizeof(V[0]), cmp); cout << Kruskal(0)<<endl; return 0; }
相关文章推荐
- 无向图基本算法 -- 遍历及最小生成树算法
- 无向图基本算法 -- 遍历及最小生成树算法
- 最小生成树基本算法---算法导论读书笔记
- 最小生成树的两种最基本的算法
- 图基本算法 最小生成树 Prim算法(邻接表/邻接矩阵+优先队列STL)
- 最小生成树,最短路径的基本算法
- hdu1233 还是畅通工程(两种基本最小生成树算法)
- 图基本算法 最小生成树 Prim算法(邻接表/邻接矩阵+优先队列STL)
- 《算法导论》笔记(12) 基本的图算法,最小生成树
- 图基本算法:深度广度遍历最小生成树
- 图的基本算法(最小生成树)
- 【算法导论】最小生成树扩展
- Prim最小生成树算法详解以及java实现源代码
- hdu 1162Eddy's picture Prime算法求最小生成树
- 浅谈最小生成树的算法思路(二)Kruskal算法
- 关于图ADT的一些算法——最小生成树算法(普利姆/Prim算法)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树 Kruskal 算法 简单题
- 最小生成树的两种寻路算法及证明[上]
- 最小生成树(克鲁斯卡尔算法)- 数据结构和算法63