贪心算法(又称贪婪算法Greedy):在对问题求解时,总是做出在当前看来是最好的选择。仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
可用贪心算法求解的问题一般有两个重要性质:
1、贪心选择性质
在当前状态下做出最好选择,即局部最优选择,然后再去解决做出这个选择后产生的响应的子问题,通常以自顶向下的方式进行,以迭代的方式做出相继的贪心选择,每做一次贪心选择就将所求问题简化为规模更小的子问题。而在动态规划算法中,每步所做的选择往往依赖于相关子问题的解(自底向上)。
2、最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
在此先提出背包问题,与0-1背包问题类似,所不同的是选择物品i装入背包时,可以选择i的一部分而不是全部。这样背包问题可以用贪心算法求解:将物品单位重量价值由高到低的顺序依次放入背包,直到背包被装满。而对于0-1背包问题,贪心选择之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每千克背包空间的价值降低了。
单源最短路径贪心算法实现:
public class MaxSum { static int MAX_SIZE=6; //设顶点数为6 public static void dijkstra(int v,float[][]a,float[]dist,int[]prev) {//v是源点,a[i][j]是边(i,j)的权,dist[i]表示当前从源到顶点i的最短特殊路径长度 //prev[i]记录的是从源点到顶点i的最短路径上i的前一个顶点 int n=dist.length-1; //n为G图中的顶点个数,问题的规模,0号元素未使用 if(v<1||v>n)return; //在所有点中选择一点作为源点v boolean []s=new boolean[n+1];//判断点是否在集合S中,一个顶点属于s(值为true)当且仅当从源到该顶点的最短路径长度已知0 for(int i=1;i<=n;i++) { dist[i]=a[v][i]; s[i]=false;//将集合s中的所有点设为false,即dist[i]不确定是否为最短路径 if(dist[i]==Float.MAX_VALUE) prev[i]=0;//说明从源点v需要经过别的点才能到达i(Float.MAX_VALUE意为大数无穷) else prev[i]=v;//有通路则让点i的前驱指向源 } dist[v]=0;s[v]=true;//初始时s中只含有v for(int i=1;i<n;i++) { float temp=Float.MAX_VALUE; int u=v; //在剩下的点中除了没有通路的点中找到最容易到达的,并把最容易到达的放入u中 for(int j=1;j<=n;j++) if((!s[j])&&(dist[j]<temp)) { u=j; temp=dist[j];//temp为所有的dist[j]的最优解 } s[u]=true; //dist[u]已确定,则可将点u放入s中去 for(int j=1;j<=n;j++) if((!s[j])&&(a[u][j]<Float.MAX_VALUE)) {//源到点j通过点u的最短特殊路径长度newdist float newdist=dist[u]+a[u][j]; if(newdist<dist[j]) {//v到j的最短路径经过u dist[j]=newdist; prev[j]=u; } } } } public static void main(String args[]) { float a[][]=new float[MAX_SIZE][MAX_SIZE];float[]dist=new float[MAX_SIZE];int []prev=new int[MAX_SIZE]; for(int i=0;i<6;i++) for(int j=0;j<6;j++) a[i][j]=Float.MAX_VALUE; a[1][2]=10; a[1][4]=30; a[1][5]=100; a[2][3]=50; a[3][5]=10; a[4][3]=20; a[4][5]=60; int v=1;//假设从顶点1处出发 dijkstra(v,a,dist,prev); System.out.println("从1出发到2、3、4、5的最短路径依次是:"); for(int j=2;j<6;j++) { System.out.println(dist[j]); } int z=prev[5],y=prev[z],x=prev[y]; System.out.println("从1到5最短路径经过的点为:"); System.out.print(x+" "+y+" "+z+" "+"5"); } }
运行结果: