Kruskal算法求最小生成森林(C++

本文详细解析了Kruskal算法,一种用于求解最小生成树的经典算法。文章通过实例介绍了算法的基本思想,即通过排序边的权重并逐一检查是否形成回路来构建最小生成树。此外,还提供了一个具体的编程实现案例,帮助读者深入理解算法的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Kruskal算法常常与prim算法放到一起讲,通常求最小生成树,本题求最小森林。本文只讲解Kruskal算法

具体参考学习网址:Kruskal详解

首先一棵生成树它是树,所以满足边是点数n-1,且生成树之间没有回路。

克鲁斯卡尔算法的具体思路是:将所有边按照权值的大小进行升序排序,
然后从小到大一一判断,条件为:如果这个边不会与之前选择的所有边组成回路,
就可以作为最小生成树的一部分;
反之,舍去。直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。
筛选出来的边和所有的顶点构成此连通网的最小生成树。

判断是否会产生回路的方法为:在初始状态下给每个顶点赋予不同的标记,对于遍历过程的每条边,其都有两个顶点,判断这两个顶点的标记是否一致,如果一致,说明它们本身就处在一棵树中,如果继续连接就会产生回路;如果不一致,说明它们之间还没有任何关系,可以连接。

以此图为例
在这里插入图片描述

对所有边的权值大小进行升序排序,那么顺序(u,v,  w)
(1,3,2)(2,3,3)(3,4,3)(1,2,5)(3,6,6)
(1,4,7)(5,6,7)(4,6,8)(3,5,9)(2,5,11)
但是kruskal则是从最小的边开始遍历所以1,3连接,然后是2 3和3 4,因为
两个顶点标记不同,即是说不是一个祖宗,所以可以相连。然后是(3,6)
因为3和6也是不同家族,所以可以相连。再看(1,4)和(5,6)因为(1,4)
已经属于一个家庭,所以不可以再连接,舍去,而5和6来自不同家庭,所以可以相连
现在发现边数恰等于点数-1,所以已经构成一棵树,故输出。

在这里插入图片描述
例题:
输入
第一行两个数n,m,q。
n代表图中点的数目,m代表边的数目,q代表查询的次数。
其中10<=n<=1000,10<=m<=200000,1<=q<=50
接下来m行,每行三个数u,v,w。
代表点u和v之间的权重为w (−50≤w≤50)
接下来q行,每行一个整数s。

输出
对于每次查询,输出s所在连通分量的最小生成树的权重和。
输入:

10 20 1
9 3 53
10 3 78
1 4 3
8 7 10
2 8 10
10 9 98
6 3 100
9 7 70
4 10 26
7 10 25
5 2 25
7 4 5
5 7 29
6 1 24
8 1 87
3 1 78
3 8 64
6 8 34
3 2 86
2 10 78
5

输出:
219
限制
1s, 10240KiB for each test case
因为不允许用STL,所以代码很冗长

#include<iostream>
using namespace std;
template<class T>
void changeLength1D(T*& a, int oldLength, int newLength)
{
   if (newLength < 0)
      exit(1);
   T* temp = new T[newLength];              // new array
   int number = min(oldLength, newLength);  // number to copy
   copy(a, a + number, temp);
   delete [] a;                             // deallocate old memory
   a = temp;
}
//小根堆 
template<class T>
class minHeap{
public:
    minHeap(int initialCapacity){
        heap=new T[initialCapacity+1];
        arrayLength=initialCapacity+1;
        heapSize=0;
    }
    int size(){return heapSize;}
    void push(const T& theElement);
    bool empty(){return heapSize==0;}
    T top(){return heap[1];}
    void pop();
    void initialize(T * theHeap,int theSize);
    void deactivateArray(){
        heap=NULL;
        arrayLength=heapSize=0;
    }
private:
    T *heap;// number of elements in queue
    int arrayLength;// queue capacity + 1
    int heapSize; // element array
};
template<class T>
void minHeap<T>::initialize(T *theHeap, int theSize){
    heap=theHeap;
    heapSize=theSize;
    arrayLength=heapSize+1;
    for(int root=heapSize/2;root>=1;root--){
        T rootElement=heap[root];
        int child=2*root;
        while(child<=heapSize){
            if(child<heapSize&&heap[child].weight>heap[child+1].weight)
                child++;
            if(rootElement.weight<=heap[child].weight)
                break;
            heap[child/2]=heap[child];
            child*=2;
        }
        heap[child/2]=rootElement;
    }
}
template<class T>
void minHeap<T>::push(const T &theElement){
// Add theElement to heap.
   // increase array length if necessary
    if(heapSize==arrayLength-1){
     // double array length
       changeLength1D(heap, arrayLength, 2 * arrayLength);
      arrayLength *= 2;
    }
    // find place for theElement
   // currentNode starts at new leaf and moves up tree
    int currentNode=++heapSize;
    while(currentNode!=1&&heap[currentNode/2].weight>theElement.weight){
        heap[currentNode]=heap[currentNode/2];
        currentNode/=2;
    }
    heap[currentNode]=theElement;
}

template<class T>
void minHeap<T>::pop(){
   // Delete min element
    heap[1].~T();
    // Remove last element and reheapify
    T lastElement=heap[heapSize--];
    // find place for lastElement starting at root
    int currentNode=1,child=2;
    while(child<=heapSize){
     // heap[child] should be smaller child of currentNode
        if(child<heapSize&&heap[child].weight>heap[child+1].weight)
            child++;
            // can we put lastElement in heap[currentNode]?
        if(lastElement.weight<=heap[child].weight)
            break;
        heap[currentNode]=heap[child];
        currentNode=child;
        child*=2;
    }
    heap[currentNode]=lastElement;
}
int Find(int *parent,int theElement){
    return parent[theElement]==theElement?theElement:parent[theElement]=Find(parent, parent[theElement]);
}
struct Edge{
    int start;
    int end;
    int weight;
    Edge(){start=0;end=0;weight=0;}
    Edge(int start,int end,int weight){
        this->start=start;
        this->end=end;
        this->weight=weight;
    }
};

int main(){
 int n,m,q;
    cin>>n>>m>>q;
    minHeap<Edge> mH(m);
    for(int i=0;i<m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        Edge e(u,v,w);
        mH.push(e);
    }
    int *pa=new int[n+1];//父亲数组 
    int *length=new int[n+1];
    for(int i=0;i<n+1;i++){
        pa[i]=i;
        length[i]=0;
    }
    int k=0;//k 记录已经连接了多少条边 
    for(int i=0;i<m;i++){
        if(k==n-1)
            break;
        Edge e=mH.top();//获取堆顶元素 
        mH.pop();//删除第一个最小数 
        int root1=Find(pa, e.start);//从根开始寻找父节点  
        int root2=Find(pa, e.end);//查找
        if(root1!=root2){    
            k++;
            pa[root2]=root1;
            length[root1]+=(length[root2]+e.weight);
            length[root2]=length[root1];
        }
    }
    for(int i=0;i<q;i++){
        int s;
        cin>>s;
        cout<<length[Find(pa, s)]<<endl;
    }
    return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值