跳到主要内容

克鲁斯卡尔算法

提示
  1. 基本原理:Kruskal算法是一种最小生成树算法,它通过选择边的子集来构建一个包含所有顶点且总权重最小的树。
  2. 操作步骤:首先,将所有边按权重从小到大排序;然后,逐一添加权重最小的边到生成树中,但拒绝会形成循环的边;重复此过程直到覆盖所有顶点。
  3. 特点与应用:作为一种贪心算法,Kruskal算法在不同领域,如电气布线和计算机网络的局域网连接中,被用于寻找最优的连接路径。

Kruskal算法是一种最小生成树算法,它接受一个图作为输入,并找到该图的边的子集,这些边:

  • 形成包括每个顶点的树
  • 具有从图中可以形成的所有树中最小权重的总和

Kruskal算法的工作原理

它属于一类称为贪心算法的算法,这类算法找到局部最优解,以期找到全局最优解。

我们从具有最低权重的边开始,不断添加边,直到达到我们的目标。

实施Kruskal算法的步骤如下:

  1. 将所有边从低权重排序到高权重
  2. 取具有最低权重的边并将其添加到生成树中。如果添加边创建了一个循环,则拒绝此边。
  3. 继续添加边,直到我们达到所有顶点。

Kruskal算法示例

从加权图开始

选择最小权重的边,如果有多个,选择任意一个

选择下一个最短的边并添加

选择下一个不会创建循环的最短边并添加

选择下一个不会创建循环的最短边并添加

重复直到生成生成树

Kruskal算法伪代码

任何最小生成树算法都围绕着检查添加一条边是否创建循环展开。

找出这一点的最常见方法是一种称为并查集的算法。并查集算法将顶点分成集群,并允许我们检查两个顶点是否属于同一集群,从而决定是否添加边会创建循环。

KRUSKAL(G):
A = ∅
对于每个顶点 v ∈ G.V:
MAKE-SET(v)
对于按照边的权重升序排序的每个边(u, v) ∈ G.E:
如果 FIND-SET(u) ≠ FIND-SET(v):
A = A ∪ {(u, v)}
UNION(u, v)
返回 A

Python、Java 和 C/C++示例

Python Java C C++

# Python中的Kruskal算法

class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = []

def add_edge(self, u, v, w):
self.graph.append([u, v, w])

# 查找函数
def find(self, parent, i):
if parent[i] == i:
return i
return self.find(parent, parent[i])

def apply_union(self, parent, rank, x, y):
xroot = self.find(parent, x)
yroot = self.find(parent, y)
if rank[xroot] < rank[yroot]:
parent[xroot] = yroot
elif rank[xroot] > rank[yroot]:
parent[yroot] = xroot
else:
parent[yroot] = xroot
rank[xroot] += 1

# 应用Kruskal算法
def kruskal_algo(self):
result = []
i, e = 0, 0
self.graph = sorted(self.graph, key=lambda item: item[2])
parent = []
rank = []
for node in range(self.V):
parent.append(node)
rank.append(0)
while e < self.V - 1:
u, v, w = self.graph[i]
i = i + 1
x = self.find(parent, u)
y = self.find(parent, v)
if x != y:
e = e + 1
result.append([u, v, w])
self.apply_union(parent, rank, x, y)
for u, v, weight in result:
print("%d - %d: %d" % (u, v, weight))


g = Graph(6)
g.add_edge(0, 1, 4)
g.add_edge(0, 2, 4)
g.add_edge(1, 2, 2)
g.add_edge(1, 0, 4)
g.add_edge(2, 0, 4)
g.add_edge(2, 1, 2)
g.add_edge(2, 3, 3)
g.add_edge(2, 5, 2)
g.add_edge(2, 4, 4)
g.add_edge(3, 2, 3)
g.add_edge(3, 4, 3)
g.add_edge(4, 2, 4)
g.add_edge(4, 3, 3)
g.add_edge(5, 2, 2)
g.add_edge(5, 4, 3)
g.kruskal_algo()
// Java中的Kruskal算法

import java.util.*;

class Graph {
class Edge implements Comparable<Edge> {
int src, dest, weight;

public int compareTo(Edge compareEdge) {
return this.weight - compareEdge.weight;
}
};

// 并查集
class Subset {
int parent, rank;
};

int vertices, edges;
Edge edge[];

// 创建图
Graph(int v, int e) {
vertices = v;
edges = e;
edge = new Edge[edges];
for (int i = 0; i < e; ++i)
edge[i] = new Edge();
}

int find(Subset subsets[], int i) {
if (subsets[i].parent != i)
subsets[i].parent = find(subsets, subsets[i].parent);
return subsets[i].parent;
}

void union(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++;
}
}

// 应用Krushkal算法
void kruskalAlgo() {
Edge result[] = new Edge[vertices];
int e = 0;
int i = 0;
for (i = 0; i < vertices; ++i)
result[i] = new Edge();

// 对边进行排序
Arrays.sort(edge);
Subset subsets[] = new Subset[vertices];
for (i = 0; i < vertices; ++i)
subsets[i] = new Subset();

for (int v = 0; v < vertices; ++v) {
subsets[v].parent = v;
subsets[v].rank = 0;
}
i = 0;
while (e < vertices - 1) {
Edge nextEdge = new Edge();
nextEdge = edge[i++];
int x = find(subsets, nextEdge.src);
int y = find(subsets, nextEdge.dest);
if (x != y) {
result[e++] = nextEdge;
union(subsets, x, y);
}
}
for (i = 0; i < e; ++i)
System.out.println(result[i].src + " - " + result[i].dest + ": " + result[i].weight);
}

public static void main(String[] args) {
int vertices = 6; // 顶点数
int edges = 8; // 边数
Graph G = new Graph(vertices, edges);

G.edge[0].src = 0;
G.edge[0].dest = 1;
G.edge[0].weight = 4;

G.edge[1].src = 0;
G.edge[1].dest = 2;
G.edge[1].weight = 4;

G.edge[2].src = 1;
G.edge[2].dest = 2;
G.edge[2].weight = 2;

G.edge[3].src = 2;
G.edge[3].dest = 3;
G.edge[3].weight = 3;

G.edge[4].src = 2;
G.edge[4].dest = 5;
G.edge[4].weight = 2;

G.edge[5].src = 2;
G.edge[5].dest = 4;
G.edge[5].weight = 4;

G.edge[6].src = 3;
G.edge[6].dest = 4;
G.edge[6].weight = 3;

G.edge[7].src = 5;
G.edge[7].dest = 4;
G.edge[7].weight = 3;
G.kruskalAlgo();
}
}
// C语言中的Kruskal算法

#include <stdio.h>

#define MAX 30

typedef struct edge {
int u, v, w;
} edge;

typedef struct edge_list {
edge data[MAX];
int n;
} edge_list;

edge_list elist;

int Graph[MAX][MAX], n;
edge_list spanlist;

void kruskalAlgo();
int find(int belongs[], int vertexno);
void applyUnion(int belongs[], int c1, int c2);
void sort();
void print();

// 应用Krushkal算法
void kruskalAlgo() {
int belongs[MAX], i, j, cno1, cno2;
elist.n = 0;

for (i = 1; i < n; i++)
for (j = 0; j < i; j++) {
if (Graph[i][j] != 0) {
elist.data[elist.n].u = i;
elist.data[elist.n].v = j;
elist.data[elist.n].w = Graph[i][j];
elist.n++;
}
}

sort();

for (i = 0; i < n; i++)
belongs[i] = i;

spanlist.n = 0;

for (i = 0; i < elist.n; i++) {
cno1 = find(belongs, elist.data[i].u);
cno2 = find(belongs, elist.data[i].v);

if (cno1 != cno2) {
spanlist.data[spanlist.n] = elist.data[i];
spanlist.n = spanlist.n + 1;
applyUnion(belongs, cno1, cno2);
}
}
}

int find(int belongs[], int vertexno) {
return (belongs[vertexno]);
}

void applyUnion(int belongs[], int c1, int c2) {
int i;

for (i = 0; i < n; i++)
if (belongs[i] == c2)
belongs[i] = c1;
}

// 排序算法
void sort() {
int i, j;
edge temp;

for (i = 1; i < elist.n; i++)
for (j = 0; j < elist.n - 1; j++)
if (elist.data[j].w > elist.data[j + 1].w) {
temp = elist.data[j];
elist.data[j] = elist.data[j + 1];
elist.data[j + 1] = temp;
}
}

// 打印结果
void print() {
int i, cost = 0;

for (i = 0; i < spanlist.n; i++) {
printf("\n%d - %d : %d", spanlist.data[i].u, spanlist.data[i].v, spanlist.data[i].w);
cost = cost + spanlist.data[i].w;
}

printf("\n生成树的成本: %d", cost);
}

int main() {
int i, j, total_cost;

n = 6;

Graph[0][0] = 0;
Graph[0][1] = 4;
Graph[0][2] = 4;
Graph[0][3] = 0;
Graph[0][4] = 0;
Graph[0][5] = 0;
Graph[0][6] = 0;

Graph[1][0] = 4;
Graph[1][1] = 0;
Graph[1][2] = 2;
Graph[1][3] = 0;
Graph[1][4] = 0;
Graph[1][5] = 0;
Graph[1][6] = 0;

Graph[2][0] = 4;
Graph[2][1] = 2;
Graph[2][2] = 0;
Graph[2][3] = 3;
Graph[2][4] = 4;
Graph[2][5] = 0;
Graph[2][6] = 0;

Graph[3][0] = 0;
Graph[3][1] = 0;
Graph[3][2] = 3;
Graph[3][3] = 0;
Graph[3][4] = 3;
Graph[3][5] = 0;
Graph[3][6] = 0;

Graph[4][0] = 0;
Graph[4][1] = 0;
Graph[4][2] = 4;
Graph[4][3] = 3;
Graph[4][4] = 0;
Graph[4][5] = 0;
Graph[4][6] = 0;

Graph[5][0] = 0;
Graph[5][1] = 0;
Graph[5][2] = 2;
Graph[5][3] = 0;
Graph[5][4] = 3;
Graph[5][5] = 0;
Graph[5][6] = 0;

kruskalAlgo();
print();
}

这段代码是使用C语言实现的Kruskal算法,用于查找带权图的最小生成树。这个算法在图论和网络设计中很有用。在输入的图中,它找到了连接所有顶点的最小权重

// C++中的Kruskal算法

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

#define edge pair<int, int>

class Graph {
private:
vector<pair<int, edge> > G; // 图
vector<pair<int, edge> > T; // 最小生成树
int *parent;
int V; // 图中顶点的数量
public:
Graph(int V);
void AddWeightedEdge(int u, int v, int w);
int find_set(int i);
void union_set(int u, int v);
void kruskal();
void print();
};
Graph::Graph(int V) {
parent = new int[V];

for (int i = 0; i < V; i++)
parent[i] = i;

G.clear();
T.clear();
}
void Graph::AddWeightedEdge(int u, int v, int w) {
G.push_back(make_pair(w, edge(u, v)));
}
int Graph::find_set(int i) {
if (i == parent[i])
return i;
else
return find_set(parent[i]);
}

void Graph::union_set(int u, int v) {
parent[u] = parent[v];
}
void Graph::kruskal() {
int i, uRep, vRep;
sort(G.begin(), G.end()); // 按权重升序排序
for (i = 0; i < G.size(); i++) {
uRep = find_set(G[i].second.first);
vRep = find_set(G[i].second.second);
if (uRep != vRep) {
T.push_back(G[i]); // 添加到最小生成树
union_set(uRep, vRep);
}
}
}
void Graph::print() {
cout << "边 :"
<< " 权重" << endl;
for (int i = 0; i < T.size(); i++) {
cout << T[i].second.first << " - " << T[i].second.second << " : "
<< T[i].first;
cout << endl;
}
}
int main() {
Graph g(6);
g.AddWeightedEdge(0, 1, 4);
g.AddWeightedEdge(0, 2, 4);
g.AddWeightedEdge(1, 2, 2);
g.AddWeightedEdge(1, 0, 4);
g.AddWeightedEdge(2, 0, 4);
g.AddWeightedEdge(2, 1, 2);
g.AddWeightedEdge(2, 3, 3);
g.AddWeightedEdge(2, 5, 2);
g.AddWeightedEdge(2, 4, 4);
g.AddWeightedEdge(3, 2, 3);
g.AddWeightedEdge(3, 4, 3);
g.AddWeightedEdge(4, 2, 4);
g.AddWeightedEdge(4, 3, 3);
g.AddWeightedEdge(5, 2, 2);
g.AddWeightedEdge(5, 4, 3);
g.kruskal();
g.print();
return 0;
}

这段代码使用C++实现了Kruskal算法,用于查找带权图的最小生成树。Kruskal算法是一种用于解决图论和网络设计问题的有效方法。该算法会按权重升序对边进行排序,然后逐步构建最小生成树。

克鲁斯卡尔算法与普里姆算法

普里姆算法 是另一种流行的最小生成树算法,它使用不同的逻辑来找到图的最小生成树(MST)。普里姆算法不是从一条边开始,而是从一个顶点开始,并持续添加不在树中的最低权重边,直到覆盖了所有顶点。

克鲁斯卡尔算法的复杂度

克鲁斯卡尔算法的时间复杂度为:O(E log E)

克鲁斯卡尔算法的应用

  • 用于布局电气布线
  • 在计算机网络中(局域网连接)