实验13 prim算法和kruskal算法

这篇具有很好参考价值的文章主要介绍了实验13 prim算法和kruskal算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🔑一个神奇的哔哩哔哩讲解链接
🔑笔记补充——第十七章:贪婪算法(思想同,但应用上和伪代码略有差别)

A:prim算法

描述

使用prim算法实现最小生成树

格式

输入

第一行两个整数n,e。n ( 1 ≤ n ≤ 200000 1 \leq n \leq 200000 1n200000) 代表图中点的个数,e ( 0 ≤ m ≤ 500000 0 \leq m \leq 500000 0m500000) 代表边的个数。
接下来e行,每行代表一条边:

  • i j w 表示顶点i和顶点j之间有一条权重为w的边

输出

最小生成树所有边的权重和

整体思路描述

Prim算法:Prim算法是从顶点出发

  • 将一个点先加入点集,遍历其所连着的边找到权值最小的边,选中该边
  • 将该边的另一个顶点加入点集
  • 再遍历这个新加入点的所有连着的边,和之前的一块比较,选出权值最小的且其另一个顶点还未加入点集的边
  • 再将其另一个顶点加入点集,以此类推,直到选了顶点数-1条边。贪心法的实验报告,实现kruskal和prim最小生成树代码,画出不同边和顶点数下运行时,# 数据结构实验合集,数据结构、算法与应用,算法,图论,数据结构

比较复杂,以OJ示例为例做如下过程演算贪心法的实验报告,实现kruskal和prim最小生成树代码,画出不同边和顶点数下运行时,# 数据结构实验合集,数据结构、算法与应用,算法,图论,数据结构

贪心法的实验报告,实现kruskal和prim最小生成树代码,画出不同边和顶点数下运行时,# 数据结构实验合集,数据结构、算法与应用,算法,图论,数据结构

实现代码

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

//-----------------10.1小根堆应用迁移start--------------------
//将一个一维数组的长度从oldLength变成newLength。(后续push操作会用到) 
template<class T>
void changeLengthID(T*& array, int oldLength, int newLength)
{
    T* newarray = new T[newLength];//函数首先分配一个新的、长度为newLength的数组   
    int number = (oldLength < newLength) ? oldLength : newLength; //取min {oldLength, newLength} 
    for (int i = 0; i < number; i++)
		newarray[i] = array[i];//然后把原数组的前min {oldLength, newLength} 个元素复制到新数组中
    delete[] array;//释放原数组所占用的空间                      
    array = newarray;//将新数组赋值到旧数组完成更改 
}

//小根堆定义及实现 
template<class T>
class minHeap
{
	public:
    	minHeap(int initialCapacity = 10)
		{//构造 
    		arrayLength = initialCapacity + 1;
    		heap = new T[arrayLength];
    		heapSize = 0;
		}
    	~minHeap() { delete[] heap; }//析构 
    	const T& top()
    	{//返回优先级最大的元素的引用 
    		return heap[1];
    	}
    	void pop();//删除
		void push(const T&theElement);//插入
    	void initialize(T*theHeap, int theSize);//初始化 
    	void output(ostream& out) const;//输出 
	private:
		T* heap;//一个类型为T的一维数组 
		int arrayLength;//数组heap的容量 
    	int heapSize;//堆的元素个数               
};

//小根堆的插入 
template<class T>
void minHeap<T>::push(const T& theElement)
{//把元素theElement加入堆
    
	//必要时增加数组长度 
	if (heapSize == arrayLength - 1)
    {//数组长度加倍 
        changeLengthID(heap, arrayLength, 2 * arrayLength);
        arrayLength *= 2;
    }
    
	//为元素theElement寻找插入位置 
	//小根堆要求老叶子比新叶子小 
    int currentNode = ++heapSize;//currentNode从新叶子向上移动,就从最底下开始 
    while (currentNode != 1 && heap[currentNode / 2] > theElement)
    {//这个时候老叶子比新叶子大,不能把元素放在这 
        heap[currentNode] = heap[currentNode / 2]; //把大的那个元素赋给currentNode,相当于把大的元素往下移 
        currentNode /= 2;//同时把currentNode(一个打算插入theElement的位置)移向双亲,就往上移                    
    }
    //循环结束,即找到合适的位置插入 
    heap[currentNode] = theElement;
}

//删除操作是针对堆顶元素而言的,即把末尾元素移动到堆顶,再自顶向下(重复构建堆的操作),递归调整。
template<class T>
void minHeap<T>::pop()
{
	//删除堆顶元素
    heap[1].~T(); 
	//删除最后一个元素,然后重新建堆(这一步相当于把末尾元素拿出来) 
    T lastElement = heap[heapSize--];
	//开始给拿出来的末尾元素找合适的放入位置,从顶开始,自顶向下调整 
    int currentNode = 1,
        child = 2;//currentNode的孩子    
    while (child <= heapSize)
    {
    	//heap[child]应该是currentNode的更大的孩子(就是说它的值太大了,应该往后头放) 
        if (child < heapSize && heap[child] > heap[child + 1])
            child++;
		
		//可以把lastElement放在heap[currentNode]吗? 
		//可以 
        if (lastElement <= heap[child])
            break; 
		//不可以(以下操作和上述push相关操作同理) 
        heap[currentNode] = heap[child];//把孩子child向上移动 
        currentNode = child;//向下移动一层寻找位置          
        child *= 2;
    }
    heap[currentNode] = lastElement;
}

//初始化一个非空小根堆 
template<class T>
void minHeap<T>::initialize(T* theHeap, int theSize)
{//在数组theHeap[1:theSize]中建小根堆(参考p304-305) 
    delete[] heap;
    heap = theHeap;
    heapSize = theSize;
	//堆化 
    for (int root = heapSize / 2; root >= 1; root--)
    {
        T rootElement = heap[root];
        int child = 2 * root; 
        while (child <= heapSize)
        {	
        	//heap[child]应该是兄弟中的较小者 
            if (child < heapSize && heap[child] > heap[child + 1])
                child++; 
			//可以把rootElement放在heap[child / 2]吗? 
			//可以(原理同上 
            if (rootElement <= heap[child])
                break;  
            //不可以 
            heap[child / 2] = heap[child]; 
            child *= 2;                   
        }
        heap[child / 2] = rootElement;
    }
}
//-------------------10.1小根堆应用迁移finish-------------------

//--------------------边结构体定义start-----------------------
struct edge//存放边
{
    long long to;//to是这个边指向的顶点 
    long long w;//w是这个边的权值 
};

struct Edge
{
 	long long w;
 	long long v;
    Edge(){}
    Edge(int w1,int v1)
	{
        w=w1;
		v=v1;
    }
    //符号重载
 	bool operator < (const Edge& b) const
 	{
  		return w < b.w;
 	}
    bool operator <= (const Edge& b) const
 	{
  		return w <= b.w;
 	}
    bool operator > (const Edge& b) const
 	{
  		return w > b.w;
 	}
};
vector<edge> edges[200005];//边的动态数组,用来存放边
bool vertex[200005];//标记是否选入 
//-------------------边结构体定义finish-----------------------

//----------------------Prim算法实现start--------------------
template<class T>
class Prim
{//定义Prim类
	public:
   		void initialize();//初始化
    	void prim();//prim算法
	private:
    	long long result=0;//权值和结果 
    	int n,e;//顶点和边数
};

template<class T>
void Prim<T>:: initialize()
{
    cin >> n >> e;//输入顶点和边数 
    for(int i = 0; i <= n; i++)
    	//顶点标记,全部初始化为true 
  		vertex[i] = true;
    for(int i = 1; i <= e; i++)
    {
    	//v1,v2是两个顶点,w是权值 
        long long v1, v2, w;
        cin >> v1 >> v2 >> w;
        //定义两个边(因为两个顶点两个方向有两条边) 
        edge e1,e2;
        //权值赋值 
        e1.w=w;
		e2.w=w;
		//顶点构造 
        e1.to=v2;
		e2.to=v1;
		//边集更新(起始点连着哪些边 
        edges[v1].push_back(e1);//在edges[v1]数组的后方插入边e1
        edges[v2].push_back(e2);//在edges[v2]数组的后方插入边e2
    }
}

template<class T>
void Prim<T>::prim()
{
	//默认先选入第一个顶点 
    long long s = edges[1].size();//第一个数组的长度
    minHeap<Edge> q;
    vertex[1] = false;//及时标记,表示已经选入 
    
    //列出选入节点的所有边,存入最小堆q 
	for(int i = 0; i < s; ++i)
    {
        long long v,w;
		v = edges[1][i].to;
		w = edges[1][i].w;
        Edge t(w,v);//临时结构体,用来存放
        q.push(t);
    }
    
    //找出上边挑出的边中权值最小的 
    for(int i = 1; i < n; i++)
    {
        long long min, to;
    	while(vertex[q.top().v] == false)
    	{//检验,如果当前最小堆最小元素所指向的将插入顶点已经是false(就是已经加入了) 
    		q.pop();//把相关数据pop掉 
		}
		//挑出最小权值,和它指向的顶点 
    	min = q.top().w;
    	to = q.top().v;
        q.pop();
        //更新结果
        result += min;
        vertex[to] = false;//更新顶点状态 
        s = edges[to].size();
        for(int j = 0; j < s; j++)
        {//遍历新加入的顶点的邻边 
            long long v = edges[to][j].to, w = edges[to][j].w;
            if(vertex[v])
            {//如果该点未被加入点集
                Edge t(w,v);
                q.push(t);
            }     
        }
    }
    cout<<result;
}
//---------------------Prim算法实现finish---------------------


int main()
{   
	Prim<int>P;
    P.initialize();
    P.prim();
    return 0;
}

B:kruskal算法

描述

使用kruskal算法实现最小生成树

格式

输入

第一行两个整数n,e。n ( 1 ≤ n ≤ 200000 1 \leq n \leq 200000 1n200000) 代表图中点的个数,e ( 0 ≤ m ≤ 500000 0 \leq m \leq 500000 0m500000) 代表边的个数。
接下来e行,每行代表一条边:

  • i j w 表示顶点i和顶点j之间有一条权重为w的边

输出

最小生成树所有边的权重和

整体思路描述

Kruskal算法:Kruskal算法是直接从边出发

  • 按权值大小排列所有边,从小到大依次选(顶点数-1)条边

  • 期间若是选中的边导致成环则丢弃,顺位往下

    • 检查是否成环用到并查集路径压缩

    贪心法的实验报告,实现kruskal和prim最小生成树代码,画出不同边和顶点数下运行时,# 数据结构实验合集,数据结构、算法与应用,算法,图论,数据结构

快速排序基本思想

贪心法的实验报告,实现kruskal和prim最小生成树代码,画出不同边和顶点数下运行时,# 数据结构实验合集,数据结构、算法与应用,算法,图论,数据结构

  • 从数列中取出一个数作为基准数

  • 分区

    • 将比这个数大的数全放到它的右边
    • 小于或等于它的数全放到它的左边
  • 再对左右区间重复第二步,直到各区间只有一个数文章来源地址https://www.toymoban.com/news/detail-761121.html

实现代码

#include<iostream>
using namespace std;

struct node//节点
{ 
	long long weight;//权重
 	int from,to;//两顶点
};
node edge[5000000];//边数组

//----------------边按权重快速排序过程start--------------------- 
void Sort(node *array,int low,int high)//排序
{
	if(low > high) return;//排好了,不运行了
	int i,j;
	node index;
	index = array[low];//定义基准数 
	i = low;
	j = high;
 	while(i < j)
	{
  		while(i < j && array[j].weight >= index.weight)
  		{
  			//从右往左找比基准数小的
   			j--;
		}	
		if(j > i) 
		{
			//交换array[i]和array[j],并把i右移一位 
			array[i++] = array[j];
		}
		while(i < j && array[i].weight < index.weight)
		{
			//从左往右找比基准数大的
			i++;
		}
		if(j > i) 
		{	
			//交换array[i]和array[j],并把j左移一位
			array[j--] = array[i];
		}
	}
 	array[i] = index;//基准点归位
 	Sort(array,low,i-1);//递归调用快排比基准点小的元素
 	Sort(array,i+1,high);//递归调用快排比基准点大的元素
}
//---------------边按权重快速排序过程finish---------------------

//--------------父顶点查找压缩,用于检验是否成环start------------- 
int pre[2000000];//记录所有节点的父节点
int find(int x)
{//查找父顶点
 	int root = x;
 	while(root != pre[root])//该顶点有父顶点
 	{
 		root = pre[root];//更新root为该点x的父顶点
	}//一直更新到最上方的顶点
	 
 	while(x != root)
	{//压缩路径,可以理解为将很高的树压缩成很矮的树,使子节点都指向最上方的顶点
  		int temp = pre[x];
  		pre[x] = root;
  		x = temp;//用于让while停止
	}
 	return root;
}
void join(int x,int y)
{//x是y的父顶点,让x的父顶点成为y的父顶点的父顶点,从而实现压缩 
 	pre[find(y)]=find(x);
}
//-------------父顶点查找压缩,用于检验是否成环finish------------- 

int main()
{   
	long long result;
 	int n,e,k = 0;//k用来记录已连接边的数量
 	cin >> n >> e;//输入顶点数和边数 
 	for(int i = 1;i <= e;i++)
 	{   
	 	cin >> edge[i].from >> edge[i].to >> edge[i].weight;
  		int temp = 0;
  		if(edge[i].from > edge[i].to)
		{//无向图按照顶点由小到大的顺序存储,如果不符合,则交换一下 
   			temp = edge[i].from;
   			edge[i].from = edge[i].to;
   			edge[i].to = temp;
  		}
 	}
 	for(int i = 1;i <= n;i++) pre[i] = i;//初始化
 	Sort(edge,1,e);//排序
 	for(int i = 1;i <= e;i++)
 	{//遍历所有的边
  		if(k==n-1) break;//终止条件,选择的边数是顶点数-1 
  		if(find(edge[i].from) != find(edge[i].to))
  		{//排除成环情况 
   			join(edge[i].from,edge[i].to);//合并顶点
   			result += edge[i].weight;//累加权值 
   			k++;//已连接边数加1 
  		}
 	}
 	cout << result << endl;
 	return 0;
}

到了这里,关于实验13 prim算法和kruskal算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 最小生成树—Kruskal算法和Prim算法

    连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任 意一对顶点都是连通的,则称此图为连通图。 生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点 和n-1条边。 最小生成树:构成生成树的

    2024年02月05日
    浏览(47)
  • 最小生成树(Prim算法与Kruskal算法)

    一个连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树称为最小生成树。 例如下图中①、②、③都是左侧图的生成树,但③是构造连通网的最小代价,所以③是该图的最小生成树。 P

    2024年02月05日
    浏览(58)
  • Prim算法和Kruskal算法到底哪个好?

    今天做了一道最小生成树的题,发现了一点猫腻! 题目在这里 : 《修路问题1》 Prim算法 和 Kruskal算法 都是从连通图中找出 最小生成树 的经典算法~ 从策略上来说, Prim算法是直接查找,多次寻找邻边的权重最小值 ,而 Kruskal是需要先对权重排序后查找的 ~ 所以说, Kru

    2024年02月05日
    浏览(32)
  • 最小(代价)生成树—Prim算法与Kruskal算法

    目录  一、最小生成树的特点 二、最小生成树算法  ① Prim(普里姆)算法 ②Kruskal(克鲁斯卡尔)算法  ③Prim算法与Kruskal算法对比 最小生成树是带权连通图G=(V,E)的生成树中边的权值之和最小的那棵生成树。它具有以下特点: 图G中各边权值互不相等时有唯一的最小生成树。图

    2024年02月01日
    浏览(38)
  • 最小生成树Kruskal、Prim算法C++

    连通图: 在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1和顶点v2是连通的。如果图中任意一对顶点都是连通的,则称此图为连通图。 生成树: 一个连通图的最小连通子图称作为图的生成树。有 n个顶点 的连通图的生成树有 n个顶点和 n-1 条边。 最小生成树: 最小生活

    2024年02月10日
    浏览(41)
  • 【最小生成树】一文学懂prim、kruskal算法

    博主简介: 努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:算法、数据结构、Java等相关知识。 博主主页: @是瑶瑶子啦 所属专栏: 算法 ;该专栏专注于蓝桥杯和ACM等算法竞赛🔥 近期目标: 写好专栏的每一篇文章 首先,我们要了解什么是最小生

    2023年04月25日
    浏览(33)
  • 用prim和kruskal算法求最小生成树问题

    题目 http://ybt.ssoier.cn:8088/problem_show.php?pid=1350 信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn) http://ybt.ssoier.cn:8088/problem_show.php?pid=1391 相当于一个图中求最小生成树的问题 prim解决 kruskal解法 信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn) http://ybt.ssoier.cn:8088/problem_show.ph

    2024年02月09日
    浏览(39)
  • 数据结构作业—第十三周---- Prim算法 Kruskal算法 Dijkstra算法

    (只看点,不看边,适合边较多的图,即 稠密图 )       是一种按权值的递增次序选择合适的边来构造最小生成树的方法;( 稀疏图 ) 适合带权有向图和带权无向图求单源最短路径; 不适合含负取值的图,求最短路径; 1 . 单选题 简单 7分 对于有n个顶点的带权连通图

    2024年02月15日
    浏览(47)
  • 第二十一章 Prim算法与Kruskal算法(通俗证明与详细讲解)

    我们先解释一下什么是最小生成树。 这个概念是基于图的,如果说存在一条路线串通起来了所有的点,那么这条路线就叫做生成树。而在这些路线中最短的那一条就叫做最小生成树。 如上图所示,图中的红色路线就是一个生成树,假设这条红色路线是众多生成树路线中最小

    2024年02月11日
    浏览(35)
  • 【算法基础:搜索与图论】3.5 求最小生成树算法(Prim&Kruskal)

    最小生成树 有关树的定义 生成子图 :生成子图是从原图中选取部分节点以及这些节点之间的边所组成的图。生成子图中的所有节点和边都必须在原图中存在。 生成树 :一个连通 无向图 的 生成子图 ,同时要求是树。也即在图的边集中选择 n - 1 条,将所有顶点连通。 我们

    2024年02月16日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包