在算法设计里面一个重要的策略就是贪心算法。贪心算法较之于动态规划是每一步求得的解是当前局部最优的解,这样一步步求下去那么所得到的解就会无限接近于全局的最优解,在某些情况下会等于最优解。
在这里主要谈谈贪心策略中的最小生成树。在嵌入式设计中,我们都会用线将各个component 连接起来,这里我们要保证每一个component 的每一个针脚我们都必须连上,这里做一个合理的假设,假设图论中的每一个节点就是component的针脚,而edge 就是连接的线,每一个线的长度和绕度等一些cost 则将它置为权重 w. 那么实际上我们已经把连接component的问题成功转化成了图论中节点、边和权重的问题了。 我们希望找到一个无环子集T(属于边集),即能够将所有的节点(针脚)连接起来,又具有最小的权重,由于子集T 是无环的,并且连接所有的节点,那么T 必然就是一个树。于是我们将由图生成的树称之为最小生成树。
而如何来生成这个树就是我接下来要讲的问题了。
假设集合A 是某颗最小生成树的一个子集,我们要做的事情就是选择一条连接2个节点边edge,将其加入到A 中,使得加入元素之后的A 依旧是某棵最小生成树的子集,由于我们可以将这条边安全的加入到子集A 中,那么称这样的边为安全边。现在剩下的就是辨别哪条边是安全边了。我们先看下图:
无向图G 的一个切割(S, V-S)是集合V 的一个划分,如果一条边的一个端点位于集合S, 而另一条边位于集合V-S,则称这条边横跨切割(S,V-S), 如果最小生成树集合A不存在横跨该切割的边,则称该切割尊重集合A 。在横跨一个切割的所有边中,权重最小的边称为轻量级边。
那么我们可以得出安全边的四个条件:
一:G 是连通无向图
二:A 包括在某棵最小生成树中
三:(S, V-S)是尊重集合A 的任意一个切割
四:边e 是横跨切割的一条轻量级边
结论:边e 是安全的
辨别安全边的理论已经分析完,那么我们下一步就是找出安全边了,在这里介绍kruskal 理论。
Kruskal理论认为将每一个结点看成是一个最小生成树,每次从图中找出权重最小的边,检查边两头的结点,如果结点分属于两个不同的集合,那么就进行合并操作,直到所有的边检测完。
#include <stdio.h> #include <stdlib.h> #define MAX 100 struct Vertex{ char data; int rank; struct Vertex *parent; }; typedef struct{ struct Vertex *u; struct Vertex *v; int w; }Edge; struct Vertex vertex[MAX]; Edge edge[MAX]; /* 文件中的测试数据:注意数据的格式和函数getdata 中输入格式的匹配 7 ABCDEFG 11 AB 7 AD 5 BC 8 BD 9 BE 7 CE 5 DE 15 DF 6 EF 8 EG 9 FG 11 */ // return n is sum of edges int GetData() { int i ,n; char ch, ch2; FILE *fin ; fin = fopen("data.txt", "rb"); // 文件中第一个数据是节点的总数 n fscanf(fin, "%d\n", &n); for(i=0; i < n; i++) { fscanf(fin, "%c", &vertex[i].data); vertex[i].rank = 0; vertex[i].parent = &vertex[i]; } // 随后输入的是边的总数 n fscanf(fin, "%d\n", &n);// n is edge of sum for(i=0; i < n; i++) { fscanf(fin, "%c%c%d\n", &ch, &ch2, &edge[i].w); edge[i].u = &vertex[ch-'A']; edge[i].v = &vertex[ch2-'A']; } fclose(fin); return n; } int cmp(const Edge *a, const Edge *b) { return (*a).w - (*b).w; } struct Vertex* Find_set(struct Vertex *x) { if(x != x->parent) x->parent = Find_set(x->parent); return x->parent; } void Link(struct Vertex *x,struct Vertex *y) { if(x->rank > y->rank) y->parent = x; else { x->parent = y; if(x->rank==y->rank) y->rank ++; } } void Union(struct Vertex *x, struct Vertex *y) { Link(Find_set(x), Find_set(y)); } void Kruskal(int n) { int i; qsort(edge, n, sizeof(Edge), cmp); for(i = 0; i < n; i++) { if(Find_set(edge[i].u) != Find_set(edge[i].v)) { printf("%c --- %c : %d \n", edge[i].u->data, edge[i].v->data, edge[i].w); Union(edge[i].u, edge[i].v); } } } int main() { Kruskal(GetData()); system("pause"); return 0; }