贪心算法—单源最短路径
Posted 算法零基础学习
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法—单源最短路径相关的知识,希望对你有一定的参考价值。
在前不久我接触了一个单源最短路径的算法,只不过那个使用动态规划思想解答。这里给大家介绍在贪心算法下的单源最短路径。
什么是单源最短路径?为什么按照这种方法求出来的答案是最优的?
又又有什么应用呢?
介绍一下单源最短路径:
在带权的有向图G=(V,E)中,其中每条边的权我们规定为非负数。给定V中的一个顶点,成为源,计算从源到其他各个顶点的最短路径长度。就是单源最短路径问题。(ps:你可能不知道图,有向图,权等这些概念,不过没关系接下来会那个例子说明)。
看这个带权的有向图:
这个就是一个很普通的带权的有向图,1 有向:通俗的说,就是方向,如果你生活在城市1,那么按照上面图的内容,你可以到一步到达城市2,但是反过来,你不能从城市2 一步到城市1,这是方向的问题。1—>2 有直达的道路,2——>1 没有直达的道路。
2带权,看到图上的数字了吗,10,100,30,60..你可以认为是距离城市1—>城市2这条边的权值为10,当然了1——>3 那就是无穷大了。
3 图 : 图的概念这里不仔细说了,大家可以自行查找资料,了解一下。这里不是我们研究的重点。
说一下算法:第一步:设置顶点集合S并不断做贪心选择来扩充这个集合,那么那些顶点能够加入到这个集合里面呢?
顶点需要满足:当且仅当从源到该顶点的最短路径已知。
每次加入到集合S之后就会修改剩下的顶点到源的最短路径。
如果你之前没了解过是不是很懵逼这样的额操作?没关系,我们举个例子:
我们选择一个点作为源点,例如选择1 ,那么我就找出从1直接到其他个点的距离中的最短的那个顶点。
S是已经选出来的最短路径点的集合,u代表的是当前我们选择的那个最短路径顶点,后面的代表通过集合S所产生的最短路径的长度。
可以看出 1——>2 最短路径为 10
1——>3最短路径为 max(无穷大没有直达的路径)
1——>4 最短路径为 30
1——>5最短路径为100
那么下一步我们选择当前的最短路径的顶点2 (10是最小的,哈哈)
S={1,2}
下一步很重点啦!
找到了2之后我们做一下工作修改 原点到剩下各个顶点的最短路径的长度
源点到3的最短路径变为:1—>2—>3 长度变为 60(60<max)
源点到4的最短路径:1—>4 不变(1—>2—>4长度为max >30)
源点到5的最短路径:1—>5 不变(1—>2—>5长度为max >100)
这个时候在剩下的里面找到最短路径的顶点4加入S;
变为
把4加入之后:
我们做一下工作修改 原点到剩下各个顶点的最短路径的长度
源点到3的最短路径变为:1—>4—>3 长度变为 60(60<max)
源点到5的最短路径:1—>4——>5 长度变为90
这个时候在剩下的里面找到最短路径的顶点3加入S;
变为
这个时候只剩下了5
那就变为了:
1—>4—>3—>5 最短长度变为60
把5加入S
所有个过程:
下面看算法:(JAVA描述核心都一样)
数组dist [ ]从源点到顶点i的最短路径的长度。
数组a[ i][ j ] 从顶点i到顶点j的权值。
v:源点下标。
数组prev[ i ] 下标i的前一个顶点。
public static void dijkstra(int v,double [][]a,double []dist,int []prev){
int n=dist.length;
if(v<0||v>n-1) return;
boolean []s=new boolean[n];
for(int i=0;i<=n-1;i++){
s[i]=false;
dist[i]=a[v][i];
if(dist[i]==Double.MAX_VALUE) prev[i]=0;
else prev[i]=v;
}
dist[v]=0;
s[v]=true;
for(int i=0;i<n-1;i++){
double temp=Double.MAX_VALUE;
int u=v;
//每次都找当前最小的 “标记” 把他加到最当前路径里面;
for(int j=0;j<=n-1;j++)
if((!s[j])&&(dist[j]<temp)){
u=j;
temp=dist[j];
}
s[u]=true;
for(int j=0;j<=n-1;j++){
if((!s[j])&&(a[u][j]<Double.MAX_VALUE)){
double newdist=dist[u]+a[u][j];
if(newdist<dist[j]){
dist[j]=newdist;
prev[j]=u;
}
}
}
}
}
下面给出上面带权图的一个完整程序:
public class Dijkstraloading {
public static void main(String[] args) {
// T ODO Auto-generated method stub
double max=Double.MAX_VALUE;
double [][]a={{0,10,max,30,100},{max,0,50,max,max},{max,max,0,max,10},{max,max,20,0,60},{max,max,max,max,0}};
double []dist=new double[5];
int []prev=new int[5];
int v;
System.out.println("请输入原点对应的下标:");
Scanner input =new Scanner (System.in);
v=input.nextInt();
dijkstra(v,a,dist,prev);
System.out.println("每个节点的情况如下:");
for(int i=0;i<dist.length;i++){
if(dist[i]==Double.MAX_VALUE)
System.out.print("此路不通!");
else
{
int j=i;
String path="";
System.out.print("原点"+v+"到"+i+ "的路径: ");
while(j!=v){
path=String.valueOf(j)+path;
j=prev[j];
}
path=String.valueOf(v)+path;
System.out.print(path);
System.out.print("该路径的长度为:"+ dist[i]);
}
System.out.println(" ");
}
}
public static void dijkstra(int v,double [][]a,double []dist,int []prev){
int n=dist.length;
if(v<0||v>n-1) return;
boolean []s=new boolean[n];
for(int i=0;i<=n-1;i++){
s[i]=false;
dist[i]=a[v][i];
if(dist[i]==Double.MAX_VALUE) prev[i]=0;
else prev[i]=v;
}
dist[v]=0;
s[v]=true;
for(int i=0;i<n-1;i++){
double temp=Double.MAX_VALUE;
int u=v;
//每次都找当前最小的 “标记” 把他加到最当前路径里面;
for(int j=0;j<=n-1;j++)
if((!s[j])&&(dist[j]<temp)){
u=j;
temp=dist[j];
}
s[u]=true;
for(int j=0;j<=n-1;j++){
if((!s[j])&&(a[u][j]<Double.MAX_VALUE)){
double newdist=dist[u]+a[u][j];
if(newdist<dist[j]){
dist[j]=newdist;
prev[j]=u;
}
}
}
}
}
}
如果你有什么问题和建议可以在下方留言:
以上是关于贪心算法—单源最短路径的主要内容,如果未能解决你的问题,请参考以下文章