实验内容
(1)用Kruskal 算法实现最小生成树
(2)画出程序运行时间与节点数n的关系图
(3)分析实验结果
Kruskal 算法
parent[1..n]←{1..n} //森林由n棵树构成,每棵树只有一个结点。
T←{ } //T为边的集合,初始时为空。
while |T|<n-1
令(x,y)为下一条边
if Find(x)≠Find(y) then
将(x,y)加入T
Union(x,y)
end if
end while
public int find(int p) {
// 直到找到根节点
while (p != parent[p]) {
p = parent[p];
}
return parent[p];
}
public void union(int p, int q) {
int pRoot = find(p);
int qRoot = find(q);
if( pRoot==qRoot ) {
// p节点、q节点的根节点一样, 故直接返回
return;
}
// 将 p节点的根节点 指向 q节点的根节点
parent[pRoot] = qRoot;
// 连通分量的数量减1
}
运行结果
如图:
顶点数为10,边数为5,随机生成树,运行之后得到最小生成树,耗时0.000126秒。
时间复杂度
实验结果
显而易见,随着节点数n增加,运行时间也迅速增加。
原因分析
在理论上,Kruskal算法的时间复杂度是O(ElogE),其中E是边的数量。这是因为算法需要对所有的边进行排序,所以时间复杂度与边的数量和对边的排序时间有关。
在实际应用中,Kruskal算法的运行时间会受到许多因素的影响,例如内存访问速度、CPU速度、磁盘I/O速度等等。因此,很难准确地预测Kruskal算法的运行时间与节点数之间的关系。一般来说,随着节点数的增加,Kruskal算法的运行时间也会相应地增加。这是因为随着节点数的增加,边的数量也会增加,从而导致排序和查找环的时间增加。
遇到的问题
算法的时间复杂度过高。
解决方法
Kruskal算法的时间复杂度为O(ElogE),其中E为边的数量。为了优化算法的时间复杂度,可以使用一些技巧,例如使用优先队列(最小堆)来存储待检查的边,这样可以减少不必要的遍历。另外,可以使用一些优化算法,例如使用并查集来处理边冲突,这样可以减少不必要的比较和合并操作。
源码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_VERTICES 100
#define MAX_EDGES 500
#define MAX_WEIGHT 100
typedef struct {
int from, to, weight;
} Edge;
typedef struct {
int parent;
int rank;
} Subset;
int find(Subset subsets[], int i) {
if (subsets[i].parent != i) {
subsets[i].parent = find(subsets, subsets[i].parent);
}
return subsets[i].parent;
}
void unionSets(Subset subsets[], int x, int y) {
int xroot = find(subsets, x);
int yroot = find(subsets, y);
if (subsets[xroot].rank < subsets[yroot].rank) {
subsets[xroot].parent = yroot;
} else if (subsets[xroot].rank > subsets[yroot].rank) {
subsets[yroot].parent = xroot;
} else {
subsets[yroot].parent = xroot;
subsets[xroot].rank++;
}
}
int compareEdges(const void *a, const void *b) {
return ((Edge*)a)->weight - ((Edge*)b)->weight;
}
void kruskal(int numVertices, int numEdges, Edge edges[]) {
Edge result[MAX_VERTICES];
Subset subsets[MAX_VERTICES];
int e = 0;
int i = 0;
qsort(edges, numEdges, sizeof(Edge), compareEdges);
for (int v = 0; v < numVertices; v++) {
subsets[v].parent = v;
subsets[v].rank = 0;
}
while (e < numVertices - 1 && i < numEdges) {
Edge nextEdge = edges[i++];
int x = find(subsets, nextEdge.from);
int y = find(subsets, nextEdge.to);
if (x != y) {
result[e++] = nextEdge;
unionSets(subsets, x, y);
}
}
printf("最小生成树的边及权值:\n");
for (int j = 0; j < e; j++) {
printf("%d - %d, 权值: %d\n", result[j].from, result[j].to, result[j].weight);
}
}
int main() {
int numVertices, numEdges;
Edge edges[MAX_EDGES];
printf("请输入顶点个数:");
scanf("%d", &numVertices);
printf("请输入边数:");
scanf("%d", &numEdges);
if (numEdges > numVertices * (numVertices - 1) / 2) {
printf("边数过多,无法构成连通图\n");
return 1;
}
srand(time(NULL));
printf("自动生成各边的权值:\n");
for (int i = 0; i < numEdges; i++) {
edges[i].from = rand() % numVertices;
edges[i].to = rand() % numVertices;
edges[i].weight = rand() % MAX_WEIGHT + 1;
printf("边%d: %d - %d, 权值: %d\n", i+1, edges[i].from, edges[i].to, edges[i].weight);
}
clock_t start, end;
start = clock();
kruskal(numVertices, numEdges, edges);
end = clock();
printf("程序运行时间:%f秒\n", (double)(end - start) / CLOCKS_PER_SEC);
return 0;
}