图论算法(四)--最小生成树的Kruskal [ 加边 ] 、Prim [ 加点 ] 的解法(JAVA)

之前我们介绍了求最短路径算法,现在又讲最小生成树算法,这两个算法有什么区别呢?

首先要明确,最短路径和最小生成树是两个不同的概念
最短路径是对于一个图的两个结点而言的。在一个图中,结点A通过某些结点和边可以走到结点B,这些结点和边组成的从A到B的路径中,最短路径就这些路径中权值总和最小的那一条(或多条)。
最短路径常用算法有:FloydDijkstra、SPFA、A*等

最小生成树是对于一个图本身而言的。对于一个有n个结点的无向连通图,必然可以去掉某些边,使得最终剩下n-1条边,与n个结点共同组成原图的一个生成树,而最小生成树就是所有可能的生成树中n-1条边的权值总和最小的那一个(或多个)。
常用算法有:KruskalPrim


我们现在在1号位置,我们要走遍图中的所有顶点,各条路上的数值即为权值,求这个过程中的最小权值是多少?

city

Input:
6 9
2 4 11
3 5 13
4 6 3
5 6 4
2 3 6
4 5 7
1 2 1
3 4 9
1 3 2
Output:
19

Kruskal

又称 “加边法”
适用于稀疏图
时间复杂度:O(MlogN),但通常边M的数目要比顶点N的数目多很多,所以最终时间复杂度为O(MlogM)

import java.util.Scanner;class edge {int u, v, w;edge(int u, int v, int w) {this.u = u;this.v = v;this.w = w;}
}
public class Kruskal {static edge[] e = new edge[10];static int n, m;static int[] f = new int[7];static int sum = 0;static int count = 0;static Scanner input = new Scanner(System.in);public static void main(String[] args) {n = input.nextInt();m = input.nextInt();for (int i = 1; i <= m; i++) {int a = input.nextInt();int b = input.nextInt();int c = input.nextInt();e[i] = new edge(a, b, c);}/*** 按权值排序* */quicksort(e, 1, m);for (int i = 1; i <= n; i++) {f[i]  = i;}kruskal();System.out.println(sum);}private static void kruskal() {/*** 从小到大枚举每一条边* */for (int i = 1; i <= m; i++) {/*** 检查一条边的两个顶点是否已经连通,即判断是否在同一个集合中* */if (merge(e[i].u, e[i].v)) {count++;sum = sum + e[i].w;}/*** 选到n-1边之后,退出循环* */if (count == n - 1) {break;}}}public static int partition(edge[] a, int p, int q) {int x = a[p].w;int i = p;for (int j = p+1; j <= q; j++) {if (a[j].w <= x) {i += 1;edge temp = a[i];a[i] = a[j];a[j] = temp;}}edge temp = a[p];a[p] = a[i];a[i] = temp;return i;}public static void quicksort(edge[] a,int p, int q) {if (p < q) {int r = partition(a ,p ,q);quicksort(a, p, r - 1);quicksort(a, r + 1, q);}}private static int getf(int v) {if (f[v] == v) {return v;} else {/*** 压缩路径,每次函数返回时,将该位置的编号转成祖宗编号* */f[v] = getf(f[v]);return f[v];}}private static boolean merge(int v, int u) {int t1 = getf(v);int t2 = getf(u);/*** 判断祖先是否相同* */if (t1 != t2) {/*** 靠左原则* */f[t2] = t1;return true;}return false;}
}

prim

又称 “加点法”
适用于稠密图
时间复杂度:O(N^2)

import java.util.Scanner;public class prim {static int[][] e = new int[7][7];static int[] book = new int[7];static int[] dis = new int[7];static int count = 0;static int sum = 0;static int n, m;static int min, mark;static Scanner input = new Scanner(System.in);public static void main(String[] args) {n = input.nextInt();m = input.nextInt();for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (i == j) {e[i][j] = 0;} else {e[i][j] = 99999999;}}}for (int i = 1; i <= m; i++) {int a = input.nextInt();int b = input.nextInt();int c = input.nextInt();e[a][b] = c;e[b][a] = c;}for (int i = 1; i <= n; i++) {dis[i] = e[1][i];}book[1] = 1;prime();System.out.println(sum);}private static void prime() {count++;while (count < n) {min = 99999999;for (int i = 1; i <= n; i++) {if(book[i] == 0 && dis[i] < min) {min = dis[i];mark = i;}}book[mark] = 1;count++;sum += dis[mark];for (int i = 1; i <= n; i++) {if (book[i] == 0 && dis[i] > e[mark][i]) {dis[i] = e[mark][i];}}}}
}

而如果借助“堆”,每次选边的时间复杂度可以变为O(logM)

方法:
数组dis用来记录生成树到各个顶点的距离。
数组h是一个最小堆,堆里面存储的是顶点的编号。这里不是按照顶点编号的大小创建的,而是按照顶点在数组dis中对应的值建立这个最小堆。
**数组po**s用来记录每个顶点的最小堆的位置
下面的左图代表,
1号结点到2号点的距离为1
1号结点到3号点的距离为2
1号结点到6号点的距离为max
1号结点到4号点的距离为max
1号结点到5号点的距离为max
下面的右图代表
dis数组存放了左图的信息
h数组存放堆
poa数组存放了顶点对应在堆中的位置
city

import java.util.Scanner;
public class prim2 {static int[] book = new int[7];static int[] dis = new int[7];static int[] h = new int[7];static int[] pos = new int[7];static int[] u = new int[19];static int[] v = new int[19];static int[] w = new int[19];static int[] first = new int[7];static int[] next = new int[19];static int count = 0, size;static int sum = 0;static int n, m;static int min, mark, mark2;static Scanner input = new Scanner(System.in);public static void main(String[] args) {n = input.nextInt();m = input.nextInt();for (int i = 1; i <= m; i++) {u[i] = input.nextInt();v[i] = input.nextInt();w[i] = input.nextInt();}/*** 无向图需要再将所有边反向存储一次* */for (int i = m + 1; i <= 2 * m; i++) {u[i] = v[i - m];v[i] = u[i - m];w[i] = w[i - m];}/*** 邻接表* */for (int i = 1; i <= n; i++) {first[i] = -1;}for (int i = 1; i <= 2 * m; i++) {next[i] = first[u[i]];first[u[i]] = i;}prime();System.out.println(sum);}private static void prime() {book[1] = 1;count++;dis[1] = 0;/*** 初始化dis数组,存放1到其他各点的距离* */for (int i = 2; i <= n; i++) {dis[i] = 99999999;}mark2 = first[1];while (mark2 != -1) {dis[v[mark2]] = w[mark2];mark2 = next[mark2];}/*** 初始化堆* */size = n;for (int i = 1; i <= size; i++) {h[i] = i;pos[i] = i;}for (int i = size / 2; i >= 1; i--) {siftdown(i);}/*** 直接弹出堆顶元素* */pop();while (count < n) {mark = pop();book[mark] = 1;count++;sum += dis[mark];/*** 扫描mark的所有边,以j为中间节点,进行松弛* */mark2 = first[mark];while (mark2 != -1) {if (book[v[mark2]] == 0 && dis[v[mark2]] > w[mark2]) {dis[v[mark2]] = w[mark2];/*** 对该点在堆中进行向上调整,pos[v[k]]是订点v[k]在堆中的位置* */siftup(pos[v[mark2]]);}mark2 = next[mark2];}}}private static void siftup(int i) {int flag = 0;/*** 堆顶* */if (i == 1) {return;}while (i != 1 && flag == 0) {/*** 当前节点是否小于父结点* */if (dis[h[i]] < dis[h[i/2]]) {swap(i, i/2);} else {flag = 1;}/*** 向上调整* */i = i/2;}}private static void swap(int x, int y) {int temp = h[x];h[x] = h[y];h[y] = temp;temp = pos[h[x]];pos[h[x]] = pos[h[y]];pos[h[y]] = temp;}private static void siftdown(int i) {int t, flag = 0;while (i * 2 <= size && flag == 0) {if (dis[h[i]] > dis[h[i*2]]) {t = i * 2;} else {t = i;}if (i * 2 + 1 <= size) {if (dis[h[t]] > dis[h[i * 2 + 1]]) {t = i * 2 + 1;}}if (t != i) {swap(t, i);i = t;} else {flag = 1;}}}private static int pop() {/*** 记录栈顶元素* */int t = h[1];pos[t] = 0;/*** 将堆底元素赋到堆顶* */h[1] = h[size];pos[h[1]] = 1;/*** 栈元素数目减 1* */size--;siftdown(1);return t;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图论算法(五)--求解割点、割边(JAVA)

割点&#xff1a;对于一个连通图来说&#xff0c;如果删除某个点之后图不再连通&#xff0c;这个点就称为割点 割点算法 时间复杂度&#xff1a;O(NM) 但是下面给出的算法时间复杂度为O(N^2)&#xff0c;这是因为下面的存储结构都是邻接矩阵&#xff0c;这样的话用该算法就完…

express+handlebars 快速搭建网站前后台

最近在重构公司网站&#xff0c;原来网站使用PHP&#xff0c;前后端不分离&#xff0c;添加与更新网站内容仍使用原始方法&#xff0c;先出布局再把调好的布局给PHP后端开发&#xff0c;花时间长&#xff0c;维护不易。因此决定将网站前后端分离&#xff0c;核心功能含网站下单…

图论算法(六)-- 二分图的最大分配问题(JAVA)

二分图&#xff1a;又称二部图&#xff0c;如果一个图的所有顶点可以被分为X和Y两个集合&#xff0c;并且所有边的两个顶点恰好一个属于一个集合X&#xff0c;另一个属于集合Y&#xff0c;即每个集合内的顶点没有边相连&#xff0c;那么这个图就是二分图。 二分图的最大分配问…

定义一个二维数组并输入数据,将二维数组元素的值按升序排列,并输出排序后的二维数组。

目录 问题&#xff1a; 回顾&#xff1a; 给出两种做法&#xff1a; 解法一&#xff1a;调用qsort 函数进行排序 代码&#xff1a; 运行结果&#xff1a; 解法二&#xff1a;冒泡排序 代码&#xff1a; 运行结果: 回顾里的4种方法的模板参考&#xff1a; 1.冒泡排序…

.net core中使用GB2312编码

引用 System.Text.Encoding.CodePages 注册 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);转载于:https://www.cnblogs.com/liushunli/p/10342521.html

实验吧-密码学-杯酒人生(特殊凯撒--维吉尼亚密码)(凯撒加解密脚本、维吉尼亚密码加解密脚本)...

题目&#xff1a; 使用古典密码 一喵星人要想喵星发送一段不知道干什么用的密码“BLOCKCIPHERDESIGNPRINCIPLE”&#xff0c; 但是它忘记了密钥是什么&#xff0c; 手头&#xff08;爪头&#xff09;只有它自己加密过的密钥“HTRUZYJW”&#xff0c; 而且它 还知道原密钥是一个…

第五届省赛(软件类)真题----Java大学C组答案及解析

第五届省赛&#xff08;软件类&#xff09;真题—-Java大学C组答案及解析 杨辉三角调和级数回文素数过大年位平方和单位分数n级台阶 一、杨辉三角 二项式的系数规律&#xff0c;我国数学家很早就发现了。 如【图1.png】&#xff0c;我国南宋数学家杨辉1261年所著的《详解九…

Angular4.x 安装|创建项目|目录结构|创建组件

Angular4.x 安装|创建项目|目录结构|创建组件 安装最新版本的 nodejs node.js 官网&#xff1a;https://nodejs.org/zh-cn/ 去官网下载 node.js&#xff0c;下一步下一步就可以了。只要 node.js 安装成功&#xff0c;那么 npm 也会帮你安装完成&#xff01; 注意&#xff1a;请…

第五届省赛(软件类)真题----Java大学B组答案及解析

第五届省赛&#xff08;软件类&#xff09;真题—-Java大学B组答案及解析 正则切分调和级数n的n次幂七对数字勾股定理九阶数独G将军 一、正则切分 java中提供了对正则表达式的支持。 有的时候&#xff0c;恰当地使用正则&#xff0c;可以让我们的工作事半功倍&#xff01; …

第五届省赛(软件类)真题----Java大学A组答案及解析

第五届省赛&#xff08;软件类&#xff09;真题—-Java大学A组答案及解析 子序列最大长度莱布尼茨公式n的n次幂七对数字勾股定理九阶数独矩阵射线 一、子序列最大长度 一个串的子串是指该串的一个连续的局部。如果不要求连续&#xff0c;则可称为它的子序列。 比如对串&…

js获取一个月的天数

在使用其他语言获取每月天数的时候&#xff0c;一般都是存储到一个数组中进行获取&#xff0c;但是如果是二月份的话就需要首先判断是否闰年&#xff0c;再确定是28还是29了。 js可以通过Date对象很方便的获取到每月的天数&#xff0c;在初始化Date对象时&#xff0c;我们可以通…

第六届省赛(软件类)真题----Java大学C组答案及解析

第六届省赛&#xff08;软件类&#xff09;真题----Java大学C组答案及解析隔行变色立方尾无穷分数循环节长度格子中输出奇妙的数字加法变乘法移动距离打印大X垒骰子 一、隔行变色 Excel表的格子很多&#xff0c;为了避免把某行的数据和相邻行混淆&#xff0c;可以采用隔行变色的…

spring_01概念及案例

1.什么是IOC?   IOC概念:inverse of Controll,控制反转,所谓控制反转,就是把创建对象和维护对象关系的权利从程序中转移到spring的容器中(applicationContext.xml),而程序本身不再维护 2.什么是di? dependency injection,依赖注入,di和IOC是一个概念,spring的设计者认为di等…

斐波那契数列(二)--矩阵优化算法

之前写了一篇从斐波那契数列分析递归与动态规划&#xff08;JAVA&#xff09;来优化斐波那契数列&#xff0c;这样可以使算法的时间复杂度从O(n^2)变到O(n),这是使用递归公式f(n)f(n-1)f(n-2)求斐波那契数列的最优算法&#xff0c;但是这只是一维世界下的极限。下面我们将其从一…

第六届省赛(软件类)真题----Java大学B组答案及解析

第六届省赛&#xff08;软件类&#xff09;真题----Java大学B组答案及解析三角形面积立方变自身三羊献瑞循环节长度九数组分数加法变乘法牌型种数饮料换购垒骰子生命之树 一、三角形面积如【图1】所示。图中的所有小方格面积都是1。那么&#xff0c;图中的三角形面积应该是多少…

【学习总结】GirlsInAI ML-diary day-11-while循环

【学习总结】GirlsInAI ML-diary 总 原博github链接-day11 认识while循环执行 对于while/break/continue的认识 新值替换变量 一般while语句 无限循环 & break continue 作业 1-更新变量 这一条没什么新的东西&#xff0c;别的语言也一样&#xff1a;python在赋值时&#x…

第六届省赛(软件类)真题----Java大学A组答案及解析

第六届省赛&#xff08;软件类&#xff09;真题----Java大学A组答案及解析熊怪吃核桃星系炸弹九数分三组循环节长度打印菱形加法变乘法牌型种数移动距离垒骰子灾后重建一、熊怪吃核桃森林里有一只熊怪&#xff0c;很爱吃核桃。不过它有个习惯&#xff0c;每次都把找到的核桃分成…

@PathVariable、@RequestParam、@RequestBody注解

讲解更加详细的参考资料 https://blog.csdn.net/u011410529/article/details/66974974 https://www.cnblogs.com/soul-wonder/p/8920553.html PathVariable注解的作用&#xff0c;获取请求地址中传递的参数&#xff0c;即&#xff1a;从URL模版中取值。 RequestParam 是从reque…

从背包问题优化详解动态规划思想

动态规划&#xff1a; 所有的数据结构与算法的理解必须建立在题目的练习上&#xff0c;否则看多少理论都没有实际用处&#xff01;&#xff01;&#xff01;所以下面这些理论文字看不懂通通没关系&#xff0c;跟随下面的背包问题还会跟深入的理解。一、基本概念&#xff1a;任何…

js判断数组里是否有重复元素的方法

转&#xff1a; js判断数组里是否有重复元素的方法 https://blog.csdn.net/longzhoufeng/article/details/78840974第一种方法&#xff1a;但是下面的这种方法数字字符串类似相同&#xff0c;返回的还是真&#xff0c;有点不靠谱&#xff0c;如果是其它的字符是可以的 var ary1…