用Fibonacci_Heap实现的minimum_spanning_tree

最小生成树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]; 
    }
    
  • 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值