B站一个Up主的动画演示非常生动形象:https://www.bilibili.com/video/BV1Eb41177d1?from=search&seid=9719056953147312216
最小生成树
最小生成树要求
- 无环图
- 联通图(任意两点之间必须有通路)
- N个顶点,N-1条边
- 所有生成树之中权值和最小的那棵(MST不唯一)
Kruskal算法
基于贪婪算法思想,每次选择权值最小的边
算法过程
1. 将所有边按照权值从小到大排序
2. 每次选择最小的边试图加入结果集
3. 如果加入后会形成环则遗弃该边(用并查集判环)
4. 重复步骤2和3直到选择了N-1条边(N为顶点数)
需要用到并查集和优先级队列
struct UFNode {
UFNode *parent;
UFNode() : parent(NULL) {}
};
UFNode *find(UFNode *p) {
set<UFNode *> path;
while (p->parent != NULL) {
path.insert(p);
p = p->parent;
}
for (set<UFNode *>::iterator it = path.begin(); it != path.end(); it++) {
(*it)->parent = p;
}
return p;
}
bool isSame(UFNode *p1, UFNode *p2) {
return find(p1) == find(p2);
}
void merge(UFNode *p1, UFNode *p2) {
if (isSame(p1, p2))
return;
find(p1)->parent = find(p2);
}
struct Node {
int src, des;
int len;
Node(int s, int d, int l) : src(s), des(d), len(l) {}
bool operator<(const Node &node)const{
return len>node.len;
}
};
//kruskal算法:
void kruskal() {
vector<UFNode> NodeArr(N);
vector<Node> ans;
int MSTLen = 0;
int s, d, l;
priority_queue<Node> pri_que;
for (int i = 0; i < M; i++) {
cin >> s >> d >> l;
pri_que.push(Node(s, d, l));
}
while (!pri_que.empty()) {
Node first = pri_que.top();
pri_que.pop();
//如果两个点已经在一个集合中,那么加入这条边会造成环
if (isSame(&NodeArr[first.src], &NodeArr[first.des])) {
continue;
}
merge(&NodeArr[first.src], &NodeArr[first.des]);
MSTLen += first.len;
ans.push_back(first);
}
cout << "MST total len is " << MSTLen << "\nthe path is :\n";
for (int i = 0; i < ans.size(); i++) {
cout << ans[i].src << "-" << ans[i].des << endl;
}
}
Prim算法
基于贪婪算法思想,每次选择已选定顶点集合到未选顶点集合权值最小的边
通过三个数组的不断更新来完成算法
注意,minDist记录的是该顶点与已选顶点集中距自己最近的顶点的距离、
算法过程:
1. 建立三个数组,selected
,minDIst
,parent
,大小为N
2. 选定0号节点为初始节点,更新其相邻节点距离信息
3. 在剩下节点中选择距离已选顶点集最近的节点,加入已选顶点集
4. 更新距离信息
5. 重复步骤3和4,直到所有顶点都被选中