图论算法(四)--最小生成树的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,一经查实,立即删除!

相关文章

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

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

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

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

第五届省赛(软件类)真题----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;则可称为它的子序列。 比如对串&…

第六届省赛(软件类)真题----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…

@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…

深入浅出讲算法思想--蛮力法思想分析及应用

蛮力法&#xff08;brute force method&#xff0c;也称为穷举法或枚举法&#xff09;是一种简单直接地解决问题的方法&#xff0c;常常直接基于问题的描述&#xff0c;所以&#xff0c;蛮力法也是最容易应用的方法。虽然&#xff0c;用蛮力法设计的算法时间特性往往也是最低的…

光耦在短距离通信中的应用

在高低压隔离系统设计中&#xff0c;难免会使用光耦来通信。在选择光耦器件时&#xff0c;需要考虑光耦允许的最大通信速率&#xff0c;否则在高速通信时会失败. 对于高速的光耦应用时&#xff0c;需要注意电流传输比率和开关速度。 参考文档《Basic Characteristics and Appli…

从NetCore报错到MySql安全

从NetCore报错到MySql安全 原文:从NetCore报错到MySql安全之前项目在测试服务器上的一些接口时不时会报出下面的错误&#xff1a;&#xff08;采用Abp框架&#xff09; "SocketException: 你的主机中的软件中止了一个已建立的连接。 STACK TRACE: at MySqlConnector.Pr…

减治法在查找算法中的应用(JAVA)--快速查找

减治法在查找算法中的应用 快速查找&#xff1a;选择问题是求一个n个数列表的第k个最小元素的问题&#xff0c;这个数k被称为顺序统计量。对于k1或kn来说&#xff0c;这并没有什么意义&#xff0c;我们通常会要找出这样的元素&#xff1a;该元素比列表中一半元素大&#xff0…

JavaScript中使用Json

转载于:https://www.cnblogs.com/lyonwu/p/10368989.html

Navicat Premium试用期破解方法(转)

转载网址https://blog.csdn.net/Jason_Julie/article/details/82864187 1、按步骤安装Navicat Premium&#xff0c;如果没有可以去官网下载&#xff1a;http://www.navicat.com.cn/download/navicat-premium 2、安装好后下载激活文件&#xff1a;https://pan.baidu.com/s/1kVgT…

减治法在生成子集问题中的应用(JAVA)--递归、二进制反射格雷码

减治法在生成组合对象问题中的应用 生成子集问题&#xff1a;经典的背包问题就是求解一个最优子集的问题&#xff0c;这里我们来讨论一个更简单的问题。对于任意一个集合来说&#xff0c;它都存在2^n个子集&#xff08;一个集合所有的子集集合称为幂集&#xff09;。 1&…

减治法解决俄式乘法问题(JAVA)

以上是在《算法设计与分析基础》一书中给出的定义。 这种算法只包括折半、加倍、相加这几个操作&#xff0c;在计算时&#xff0c;不需要用九九乘法表 。 同时&#xff0c;这个方法每次都会将计算的规模减少&#xff0c;运用了减治的思想 public class Main {public static…