最小生成树MST
A、Prim算法
一、实验目标
实现基于Fibonacci Heap的Prim算法
二、设计思路
数据结构
用path来存储图中的边,edge为图的边集
struct path
{
int a,b,w;
}edge[max_];
node来记录图中的每个节点
- key :Prim算法中的key值
- id :节点在图中的标号
- precessor :该节点的父节点
- 重载运算符的操作是为了实现Fibonacci_Heap中比较的操作
struct node
{
int key;
int id;
int precessor;
node(int k = 0,int i = 0, int Pi = 0):key(k),id(i),precessor(Pi){};
bool operator < (node t){return key<t.key;}
bool operator == (node t){return key == t.key;}
bool operator <= (node t){return key <= t.key;}
bool operator > (node t){return key > t.key;}
bool operator >= (node t){return key >= t.key;}
};
Visited用来标记图中节点是否在Q中
bool visited[max_];
算法设计
参考《算法导论》中的实现过程
-
初始化图中元素
- key 为正无穷
- 父节点为空
-
跟新根节点的key为0
-
Q = G.V
-
寻找连接每一个不在树A中的节点v与树中节点的最小边
-
跟新v的key的时候,实际上是用decrease_key实现的
- Decrease_key中包含了判断跟新的值与原本值大小的部分,于是不需要在代码中显式的再判断
-
每次extract_min以后,在visted里标记该点来记录u已经不在Q中了
- 用visited判断v是否在Q中只需O(1)
-
//Q = G.V
for(int i = 0;i < n;i++)
{
Q.Push(i,node(max_,i,i));
Adj.push_back(vector<path>());
}
//r.key = 0;
visited[0] = 1;
Q.Decrease_Key(0,node(0,0,0));
while(!Q.Empty())
{
node u = Q.Extract_Min();
if(u.id!=0)
cout<<u.precessor<<" "<<u.id<<endl;
visited[u.id] = 1;
for(int i = 0;i < Adj[u.id].size();i++)
{
int v = Adj[u.id][i].b;
if(visited[v] == 0 )
{
Q.Decrease_Key(v,node(Adj[u.id][i].w,v,u.id));
}
}
}
三、实验结果展示
B、Kruskal算法
一、实验目标
基于Path-Compression实现的并查集的Kruskal算法
二、设计思路
数据结构
并查集(disjoint set)
p数组存储每个元素的父节点
rank数组存储每个元素的秩
int p[size]={0}; //.p
int rank[size]={0}; // rank
函数实现
-
find_set(int x)
- Two-pass method
- 第一趟递归时,向上寻找,找到根
- 第二趟回溯时,向下跟新节点,使每个节点直接指向根
int find_set(int x) { if(x != p[x]) p[x] = find_set(p[x]); return p[x]; }
- Two-pass method
-
link(int x, int y)
void link(int x,int y) { if(rank[x] > rank[y]) { p[y] = x; } else{ p[x] = y; if(rank[x] == rank[x]) rank[y]++; } }
-
Union(int x, int y)
- 取决于两个节点是否具有相同的秩
void Union(int x,int y) { link(find_set(x),find_set(y)); }
算法设计
A为空集
vector<path>A;
对于图中每一个元素make-set
//make set
for(int i = 0;i < n;i++)
p[i] = i;
按照非降序对边集排序
//sorting the edges in nodecreasing order by weight w
std::qsort((void *)edge,m,sizeof(path),cmp);
判断u、v是否属于同一棵树,不是的话就连边
for(int i = 0;i < m;i++)
{
int u = edge[i].a, v = edge[i].b;
if(find_set(u) != find_set(v))
{
Union(u,v);
A.push_back(edge[i]);
}
}
Fibonacci_Heap
#pragma once
#include <vector>
#include <cstring>
#include <algorithm>
#include <map>
#include <iostream>
using namespace std;
template <class T>
class Fibonacci_Heap {
private:
struct Node {
T key;
int degree;
bool mark;
Node *p, *child, *left, *right;
Node(T k) : key(k), degree(0), mark(false) {
p = child = nullptr;
left = right = this;
}
};
Node *Min;
int n;
//map<T, Node*> mp;
vector<Node*>mp;
void Del_Tree(Node *root);
void Consolidate();
void Link(Node *y, Node *x);
void Cut(Node *x, Node *y);
void Cascading_Cut(Node *y);
public:
Fibonacci_Heap();
~Fibonacci_Heap();
void Push(int id,T x);
bool Empty();
T Top();
void Pop();
void Decrease_Key(int id, T k);
T Extract_Min();
};
template <class T>
Fibonacci_Heap<T>::Fibonacci_Heap() {
Min = nullptr;
n = 0;
}
template<class T>
T Fibonacci_Heap<T>::Extract_Min()
{
T ans = Top();
Pop();
return ans;
}
template <class T>
void Fibonacci_Heap<T>::Del_Tree(Node *root) {
if(root -> child != nullptr) {
Node *ptr = root -> child;
do {
Del_Tree(ptr);
ptr = ptr -> right;
} while(ptr != root -> child);
}
delete root;
}
template <class T>
Fibonacci_Heap<T>::~Fibonacci_Heap() {
mp.clear();
Node *ptr = Min;
if(ptr == nullptr)return;
do {
Del_Tree(ptr);
ptr = ptr -> right;
} while(ptr != Min);
}
template <class T>
void Fibonacci_Heap<T>::Push(int id,T x) {
while(id >= mp.size()){
mp.push_back(nullptr);
}
Node* point = new Node(x);
mp[id] = point;
if(n == 0){
Min = point;
}else{
Node* tmp = Min->left;
tmp->right = point;
Min->left = point;
point->left = tmp;
point->right = Min;
if(Min->key > point->key)
{
Min = point;
}
}
n++;
}
template <class T>
bool Fibonacci_Heap<T>::Empty() {
if(n == 0)return true;
else return false;
}
template <class T>
T Fibonacci_Heap<T>::Top() {
return Min->key;
}
template <class T>
void Fibonacci_Heap<T>::Pop() {
if(n == 0) return;
n--;
if(n == 0) {
delete Min;
Min = nullptr;
return;
}
Node *tmp = Min -> child;
vector <Node *> chdlist;
if(tmp != nullptr)
do{
chdlist.push_back(tmp);
tmp = tmp -> right;
}while(tmp != Min -> child);
for(int i = 0; i < chdlist.size(); i++){
Node *iterat = chdlist[i];
Node *Mleft = Min -> left;
iterat -> p = nullptr;
Mleft -> right = iterat; Min -> left = iterat;
iterat -> left = Mleft; iterat -> right = Min;
}
Node *l = Min -> left;
Node *r = Min -> right;
l -> right = r;
r -> left = l;
delete Min;
Min = l;
Consolidate();
}
template <class T>
void Fibonacci_Heap<T>::Consolidate() {
//cout<<"consolidate"<<endl;
vector<Node*>root_list;
vector<Node*>A;
Node* cur = Min->right;
root_list.push_back(Min);
while(cur != Min)
{
while(cur->degree+1 > A.size())
{ A.push_back(nullptr); }
root_list.push_back(cur);
cur = cur->right;
}
for(int i = 0;i < root_list.size();i++)
{
Node* x = root_list[i];
int d = x->degree;
while(d + 10>A.size()){A.push_back(nullptr);}
//cout<<d<<endl;
//cout<<A.size()<<endl;
while(A[d] != nullptr)
{
Node* y = A[d];
if(x->key > y->key)
{
Node* swap = x;
x = y;
y = swap;
}
Link(y,x);
A[d] = nullptr;
d++;
}
while(d+5 > A.size()){ A.push_back(nullptr);}
A[d] = x;
}
Min = nullptr;
for(int j = 0;j < A.size();j++)
{
if(A[j]!=nullptr){
if(Min == nullptr)
{
Min = A[j];
Min->left = Min;Min->right = Min;
}
else
{
Node* le = Min->left;
le -> right = A[j];
Min -> left = A[j];
A[j]->right = Min;
A[j]->left = le;
if(Min->key > A[j]->key)Min = A[j];
}
}
}
}
template <class T>
void Fibonacci_Heap<T>::Link(Node *y, Node *x) {
//remove y from the root list
Node* l = y->left;
Node* r = y->right;
l->right = r;
r->left = l;
//make y a child of x, incrementing x.degree
x->degree++;
y->p = x;
y->mark = false;
if(x->child != nullptr){
Node* t = x->child->right;
t->left = y;
y->left = x->child;
y->right = t;
x->child->right = y;
}else
{
x->child = y;
y->left = y;y->right = y;
}
}
template<class T>
void Fibonacci_Heap<T>::Decrease_Key(int id, T k) {
if(id>=mp.size()||mp[id] == nullptr){
//cout<<"The target doesn't exit"<<endl;
return;
}
if(mp[id]->key < k){
//cout<<"The key of target is higher than you thought"<<endl;
return;
}
Node* target = mp[id];
target->key = k;
Node* fa = target->p;
if(fa!=nullptr && target->key < fa->key)
{
Cut(target,fa);
Cascading_Cut(fa);
}
if(target->key < Min->key)
Min = target;
}
template <class T>
void Fibonacci_Heap<T>::Cut(Node *x, Node *y) {
y->degree--;
//remove x from child list of y
Node* tmp = x;
if(y->degree == 0){y->child = nullptr;}
else{
if(y->child == x)
y->child = x->right;
Node* left = x->left;
Node* right = x->right;
right->left = left;
left->right = right;
}
//add x to the root list
x->p = nullptr;
x->mark = false;
Node* temp = Min->left;
temp->right = x;
Min->left = x;
x->left = temp;
x->right = Min;
}
template <class T>
void Fibonacci_Heap<T>::Cascading_Cut(Node *y) {
Node* z = y->p;
if(z!=nullptr){
if(y->mark == false)
{
y->mark = true;
}else
{
Cut(y,z);
Cascading_Cut(z);
}
}
}
Prim算法
#include<iostream>
#include"Fibonacci_Heap.h"
const int max_ = 9999999;
struct path
{
int a,b,w;
}edge[max_];
struct node
{
int key;
int id;
int precessor;
node(int k = 0,int i = 0, int Pi = 0):key(k),id(i),precessor(Pi){};
bool operator < (node t){return key<t.key;}
bool operator == (node t){return key == t.key;}
bool operator <= (node t){return key <= t.key;}
bool operator > (node t){return key > t.key;}
bool operator >= (node t){return key >= t.key;}
};
bool visited[max_];
int main(int argc, const char** argv) {
Fibonacci_Heap<node>Q;
vector< vector<path> >Adj;
int n,m;
cin>>n>>m;
//Q = G.V
for(int i = 0;i < n;i++)
{
Q.Push(i,node(max_,i,i));
Adj.push_back(vector<path>());
}
//load the map
for(int i = 0;i < m;i++)
{
int a,b,w;
cin>>a>>b>>w;
edge[i].a = a;
edge[i].b = b;
edge[i].w = w;
Adj[a].push_back(edge[i]);
Adj[b].push_back((path){b,a,w});
}
//r.key = 0;
visited[0] = 1;
Q.Decrease_Key(0,node(0,0,0));
while(!Q.Empty())
{
node u = Q.Extract_Min();
if(u.id!=0)
cout<<u.precessor<<" "<<u.id<<endl;
visited[u.id] = 1;
for(int i = 0;i < Adj[u.id].size();i++)
{
int v = Adj[u.id][i].b;
if(visited[v] == 0 )
{
Q.Decrease_Key(v,node(Adj[u.id][i].w,v,u.id));
}
}
}
return 0;
}
Kruskal算法
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<fstream>
using std::cin;
using std::vector;
const int size = 10000;
const int large_size = 100000;
struct path
{
int a,b,w;
}edge[large_size];
int p[size]={0}; //.p
int rank[size]={0}; // rank
int cmp(const void * a,const void * b)
{
return ((path *)a)->w - ((path *)b)->w;
}
int find_set(int x)
{
if(x != p[x]) p[x] = find_set(p[x]);
return p[x];
}
void link(int x,int y)
{
if(rank[x] > rank[y])
{
p[y] = x;
}
else{
p[x] = y;
if(rank[x] == rank[x])
rank[y]++;
}
}
void Union(int x,int y)
{
link(find_set(x),find_set(y));
}
int main(int argc, const char** argv) {
int n,m; //n is number of vertexes. m is number of edges
cin>>n>>m;
vector<path>A;
//load the map
for(int i = 0;i < m;i++)
{
int a,b,w;
cin>>a>>b>>w;
edge[i].a = a;
edge[i].b = b;
edge[i].w = w;
}
//make set
for(int i = 0;i < n;i++)
p[i] = i;
//sorting the edges in nodecreasing order by weight w
std::qsort((void *)edge,m,sizeof(path),cmp);
for(int i = 0;i < m;i++)
{
int u = edge[i].a, v = edge[i].b;
if(find_set(u) != find_set(v))
{
Union(u,v);
A.push_back(edge[i]);
}
}
//print
for(int i = 0;i < n-1;i++)
std::cout << A[i].a<<" "<<A[i].b << std::endl;
return 0;
}