300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 贪心算法解决单源最短路径问题

贪心算法解决单源最短路径问题

时间:2022-01-11 18:43:22

相关推荐

贪心算法解决单源最短路径问题

参考教材:算法设计与分析(第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[]truefalsefalsefalsefalse对数组进行必要的修改:1 2 3 4 5prev[] 1 1 2 1 1dist[] 0 10 60 30 100s[]truetruefalsefalsefalse对数组进行必要的修改:1 2 3 4 5prev[] 1 1 4 1 4dist[] 0 10 50 30 90s[]truetruefalsetruefalse对数组进行必要的修改:1 2 3 4 5prev[] 1 1 4 1 3dist[] 0 10 50 30 60s[]truetruetruetruefalse对数组进行必要的修改:1 2 3 4 5prev[] 1 1 4 1 3dist[] 0 10 50 30 60s[]truetruetruetruetrue

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[]truefalsefalsefalsefalsefalse找到边3,1对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 5 6 4closest[] 0 3 1 1 3 3s[]truefalsetruefalsefalsefalse找到边6,3对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 6 4closest[] 0 3 1 6 3 3s[]truefalsetruefalsefalsetrue找到边4,6对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 6 4closest[] 0 3 1 6 3 3s[]truefalsetruetruefalsetrue找到边2,3对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 3 4closest[] 0 3 1 6 2 3s[]truetruetruetruefalsetrue找到边5,2对数组进行必要的修改:1 2 3 4 5 6lowcost[] 0 5 1 2 3 4closest[] 0 3 1 6 2 3s[]truetruetruetruetruetrue

1

2. Kruskal算法

Kruskal算法是构造最小生成树的另一个常用算法。

(这个大坑先留着)

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。