贪心算法—单源最短路径

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;

}

}

}

}

}

}

如果你有什么问题和建议可以在下方留言:

以上是关于贪心算法—单源最短路径的主要内容,如果未能解决你的问题,请参考以下文章

贪心算法(Dijkstra)解决单源最短路径问题(C++)

[C++]单源最短路径:迪杰斯特拉(Dijkstra)算法(贪心算法)

单源最短路径

单源最短路径_贪心算法

单源最短路径Dijkstra算法的思想详细步骤代码

单源最短路-dijkstra的算法