贪心算法解决单源最短路径问题
参考教材:算法设计与分析(第3版) 王晓东 编著 清华大学出版社
贪心算法总是做出在当前看来最好的选择,也就是说贪心算法并不从整体最优考虑,它所做出的选择只是在某种意义上的局部最优选择。
贪心算法的基本要素
1. 贪心选择性质
指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
2. 最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
拿单源最短路径当例子,从顶点5到顶点1的最短路径假设是5->4->2->1,那么,顶点4到1的最短路径一定是4->2->1,顶点2到1的最短路径一定是2->1。这种性质就叫做最优子结构性质。
问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
单源最短路径
Dijkstra算法是解单源最短路径问题的贪心算法。
算法思想的简单描述:要找出源到其他顶点的最短距离,首先将所有顶点划分成两个集合,S是已经到达的顶点,V是没有到达的顶点,显然S+V就是所有顶点。初始,S集合中只包含源顶点(本例中是1号顶点),然后找出V中距离S集合最近的一个顶点(即贪心选择,至于为什么是S集合,请务必理解该问题的最优子结构性质)。那么显然,需要有个对象来记录每个顶点到源顶点的距离,这就是代码中的dist数组。dist[2]=x就表示,顶点2到源顶点的最短距离是x。找到后,记录下路径。如何记录,同样需要一个对象,即代码中的prev数组。prev[2]=y就表示,顶点2到源顶点的最短路径中,顶点2的前一个顶点是顶点y。是不是和链表有点像?记录下路径的同时是不是还需要将该顶点加入到S集合中呢?它就是s数组了。由于简单,不多说。
测试数据:
代码:
public class Dijkstra {static float max = Float.MAX_VALUE;/*** * @param v 源* @param a 图* @param dist 路径长度* @param prev 路径*/public static void dijkstra(int v, float[][] a, float[] dist, int[] prev) {// v是源,dist[i]表示当前从源到顶点i的最短特殊路径长度,prev[i]=j:最短路径中顶点i的前一个顶点是j,类似于链表int n = dist.length - 1;// 节点个数if (v < 1 || v > n)return;boolean[] s = new boolean[n + 1];// 初始化for (int i = 1; i <= n; i++) {dist[i] = a[v][i];s[i] = false;if (dist[i] == Float.MAX_VALUE)prev[i] = 0;elseprev[i] = v;}dist[v] = 0;s[v] = true;for (int i = 1; i < n; i++) {// 循环n-1次float temp = Float.MAX_VALUE;int u = v;for (int j = 1; j <= n; j++) {// 寻找不在集合内且距离集合最近的节点jif ((!s[j]) && (dist[j] < temp)) {u = j;// 记录节点temp = dist[j];// 记录最短特殊路径长度}}s[u] = true;// 将节点u放入集合for (int j = 1; j <= n; j++) {// 重新设置dist[]和prev[]的值if ((!s[j]) && (a[u][j] < Float.MAX_VALUE)) {// 寻找不在集合内,且可达的节点float newdist = dist[u] + a[u][j];if (newdist < dist[j]) { // 与旧值进行比较,保留小的值dist[j] = newdist;prev[j] = u;}}}}}public static void main(String[] args) {float[][] a = { { max, max, max, max, max, max },{ max, 0, 10, max, 30, 100 }, { max, max, 0, 50, max, max },{ max, max, max, 0, max, 10 }, { max, max, max, 20, 0, 60 },{ max, max, max, max, max, 0 } };int n = a.length;float[] dist = new float[n];int[] prev = new int[n];dijkstra(1, a, dist, prev);System.out.println(" 顶点1到5的最短路径:");trace(prev, 5);System.out.println();System.out.println(" 顶点1到3的最短路径:");trace(prev, 3);}public static void trace(int[] prev, int n) {if (n == 1) {System.out.print(n + " ");return;}trace(prev, prev[n]);System.out.print(n + " ");}
}
- 1
测试数据运行结果:
顶点1到5的最短路径:
1 4 3 5 顶点1到3的最短路径:
1 4 3
- 1
将其中的一些运算步骤打印出来后,如下:
其中,prev数组记录路径。如果要找出顶点1到5的最短路径,可以从数组prev得到顶点5的前一个顶点是3,3的前一个订单是4,4的前一个顶点是1。于是从顶点1到5的最短路径为1,4,3,5.
dist数组记录当前顶点距离源的最短路径长度。
s[1]=true表示1顶点已经计算出最短路径了,不需要再计算了。
初始化:1 2 3 4 5prev[] 1 1 0 1 1dist[] 0 10 max 30 100s[] true false false false false对数组进行必要的修改:1 2 3 4 5prev[] 1 1 2 1 1dist[] 0 10 60 30 100s[] true true false false false对数组进行必要的修改:1 2 3 4 5prev[] 1 1 4 1 4dist[] 0 10 50 30 90s[] true true false true false对数组进行必要的修改:1 2 3 4 5prev[] 1 1 4 1 3dist[] 0 10 50 30 60s[] true true true true false对数组进行必要的修改:1 2 3 4 5prev[] 1 1 4 1 3dist[] 0 10 50 30 60s[] true true true true true
- 1
最小生成树
1. Prim算法
算法的简单描述:与单源最短路径类似。同样将顶点分成两个集合,S和V。初始,S集合只包含顶点1,然后找出V集合中距离S集合距离最短的顶点,所以同样需要有对象保存顶点到集合S的距离,即lowcost数组。lowcost[2]=x就表示,顶点2距离集合S的最短距离是x。找到后,需要记录路径,即closest数组。closest[2]=y就表示,顶点2距离集合S中最近的顶点是y。同样,也需要一个变量来表示一个顶点是属于哪个集合,即s数组。
测试数据:
代码:
public class Prim {/*** @param n 图顶点个数* @param c 图的二维数组*/public static void prim(int n,float [][] c){float [] lowcost=new float [n+1];int [] closest=new int [n+1];boolean [] s=new boolean[n+1];//初始化s[1]=true; //以第一个节点为起点for(int i=2;i<=n;i++){lowcost[i]=c[1][i];closest[i]=1;s[i]=false;}for(int i=1;i<n;i++){ //循环n-1次找出剩余n-1个节点float min=Float.MAX_VALUE;int j=1;//找集合外与集合最近的节点for(int k=2;k<=n;k++){if((lowcost[k]<min)&&(!s[k])){min=lowcost[k];j=k;}}System.out.println("找到边"+j+","+closest[j]);s[j]=true;//找离j最近的节点k,寻找与集合最近的节点for(int k=2;k<=n;k++){if((c[j][k]<lowcost[k])&&(!s[k])){lowcost[k]=c[j][k]; //记录权值closest[k]=j; //记录节点}}}}public static void main(String[] args){float [][] c={{100,100,100,100,100,100,100},{100,100,6,1,5,100,100},{100,6,100,5,100,3,100},{100,1,5,100,5,6,4},{100,5,100,5,100,100,2},{100,100,3,6,100,100,6},{100,100,100,4,2,6,100}};prim(6,c);}
}
- 1
打印运算时的中间数据,如下:
初始化:1 2 3 4 5 6lowcost[] 0 6 1 5 100 100closest[] 0 1 1 1 1 1s[] true false false false false false
找到边3,1
对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 5 6 4closest[] 0 3 1 1 3 3s[] true false true false false false
找到边6,3
对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 6 4closest[] 0 3 1 6 3 3s[] true false true false false true
找到边4,6
对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 6 4closest[] 0 3 1 6 3 3s[] true false true true false true
找到边2,3
对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 3 4closest[] 0 3 1 6 2 3s[] true true true true false true
找到边5,2
对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 3 4closest[] 0 3 1 6 2 3s[] true true true true true true
- 1
2. Kruskal算法
Kruskal算法是构造最小生成树的另一个常用算法。
(这个大坑先留着)
贪心算法解决单源最短路径问题相关推荐
- 【算法设计与分析】贪心算法:单源最短路径和prim算法的最小生成树
1.单源最短路径问题的问题提出是,计算带权有向图G =(V, E)中一个点(源点)到其余各顶点的最短路径长度,如下图所示.设源点为顶点1,采用Dijkstra算法求下图中源V0为到其余各顶点的最短路径 ...
- 图的基本算法(单源最短路径)
在许多路由问题中,寻找图中一个顶点到另一个顶点的最短路径或最小带权路径是非常重要的提炼过程.正式表述为,给定一个带权有向图G = (V, E) , 顶点s到v中顶点t的最短路径为在边集E中连接s到t代 ...
- 【算法设计与分析】分支限界法解决单源最短路径问题:输入带权图G=(V,E)以及出发顶点s,然后用分支限界法解决问题,要求输出路径和长度以及计算时间;
目的: 1.掌握分支限界法的基本思想: 2.掌握解决单源最短路径问题的分支限界法实现方法: 3.学会分析算法的时间复杂度: 4.学会用分支限界法解决实际问题. 要求: 1.输入带权图G=(V,E)以及 ...
- Dijkstra算法求单源最短路径
1.最短路径 在一个连通图中,从一个顶点到另一个顶点间可能存在多条路径,而每条路径的边数并不一定相同.如果是一个带权图,那么路径长度为路径上各边的权值的总和.两个顶点间路径长度最短的那条路径称为两个顶 ...
- 【算法】单源最短路径和任意两点最短路径总结(补增:SPFA)
[Bellman-Ford算法] [算法]Bellman-Ford算法(单源最短路径问题)(判断负圈) 结构: #define MAX_V 10000 #define MAX_E 50000 int ...
- 51nod 1445 变色DNA ( Bellman-Ford算法求单源最短路径)
1445 变色DNA 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 有一只特别的狼,它在每个夜晚会进行变色,研究发现它可以变成N种颜色之一,将这些颜色标号为0,1 ...
- dijkstra算法PHP,单源最短路径(dijkstra算法)php实现
做一个医学项目,其中在病例评分时会用到单源最短路径的算法.单源最短路径的dijkstra算法的思路如下: 如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点.那么(Vi ...
- Dijkstra(迪杰斯特拉)算法求单源最短路径问题
Dijkstra(迪杰斯特拉)算法求单源最短路径问题 重要的事情说三遍:代码不是我写的!代码不是我写的!代码不是我写的! 第一个算法是严蔚敏数据结构(C语言版)上写的,第二个算法是王道数据结构上写的, ...
- 【2023王道数据结构】【图】通过C++实现图的BFS(广度优先遍历)算法求单源最短路径问题C、C++完整实现(可直接运行)
~~~笔锋至此又怎能平淡而终,故事开始便不承认普通✌✌✌ ✌ 题目及题解持续更新中 [2023王道数据结构目录]课后算法设计题C.C++代码实现完整版大全 题目: 通过C++实现图的BFS(广度优先遍 ...
最新文章
- 千万级并发HAproxy均衡负载系统介绍
- hdoj1176【DP】
- Hibernate中常见的异常
- 漫步线性代数十一—— 四个基本子空间
- 欢迎关注我的微信公众账号:Linux技巧(微信号:irefactoring),一起学习Linux知识~~~...
- Android View 自定义RangeSeekBar范围选择器
- ArcGIS 字段计算器保留一位小数和获取字段长度
- Java互联网医院源码,以互联网方式整合优质医生资源,为患者提供MDT多学科会诊、专家咨询、复诊配药等服务。
- IRQL深入解析--IRQL级别
- 美式口语发音技巧:《连读》
- 葫芦时刻_Hulu将提供离线观看(最终)
- pytorch笔记:构建LSTM网络,实现训练验证和测试过程
- Carla中文版社区来了
- C语言邮箱名字用什么存储,用c++定义一个描述学生通讯录的类数据成员包括姓名学校电话号码和邮箱...
- quartz集群模式下qrtz_triggers表trigger_state变ERROR分析
- Kitty猫基因编码
- 电子元件-发光二极管与数码管
- 到机房维修服务器完全纪实
- Windows启动原理
- 《SOC芯片研究框架》深度科普,发展趋势、技术特点、产业链一文看懂