贪心算法.docx
- 文档编号:20151995
- 上传时间:2023-04-25
- 格式:DOCX
- 页数:17
- 大小:22.70KB
贪心算法.docx
《贪心算法.docx》由会员分享,可在线阅读,更多相关《贪心算法.docx(17页珍藏版)》请在冰豆网上搜索。
贪心算法
一、[实验目的]
掌握贪心算法的基本思想
掌握贪心算法中贪心选择性质和最优子结构性质的分析与证明
掌握贪心算法求解问题的方法
二、[实验内容]
哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。
给出文件中各个字符出现的频率,求各个字符的哈夫曼编码方案。
给定带权有向图G=(V,E),其中每条边的权是非负实数。
另外,还给定V中的一个顶点,称为源。
现在要计算从源到所有其他各顶点的最短路长度。
这里路的长度是指路上各边权之和。
设G=(V,E)是无向连通带权图,即一个网络。
E中每条边(v,w)的权为c[v][w]。
如果G的子图G’是一棵包含G的所有顶点的树,则称G’为G的生成树。
生成树上各边权的总和称为该生成树的耗费。
在G的所有生成树中,耗费最小的生成树称为G的最小生成树。
求G的最小生成树。
三、[算法原理]
1.哈夫曼编码
1.1前缀码
对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀。
这种编码称为前缀码。
编码的前缀性质可以使译码方法非常简单。
表示最优前缀码的二叉树总是一棵完全二叉树,即树中任一结点都有2个儿子结点。
平均码长定义为:
使平均码长达到最小的前缀码编码方案称为给定编码字符集C的最优前缀码。
1.2.构造哈夫曼编码
哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码。
哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。
算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T。
2.单源最短路径
2.1.算法基本思想
Dijkstra算法是解单源最短路径问题的贪心算法。
其基本思想是,设置顶点集合S并不断地作贪心选择来扩充这个集合。
一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。
初始时,S中仅含有源。
设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。
Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。
一旦S包含了所有V中顶点,dist就记录了从源到所有其他顶点之间的最短路径长度。
2.2.算法的正确性和计算复杂性
(1)贪心选择性质
(2)最优子结构性质(3)计算复杂性。
3.最小生成树
3.1.最小生成树性质
用贪心算法设计策略可以设计出构造最小生成树的有效算法。
本节介绍的构造最小生成树的Prim算法和Kruskal算法都可以看作是应用贪心算法设计策略的例子。
尽管这2个算法做贪心选择的方式不同,它们都利用了下面的最小生成树性质:
设G=(V,E)是连通带权图,U是V的真子集。
如果(u,v)E,且uU,vV-U,且在所有这样的边中,(u,v)的权c[u][v]最小,那么一定存在G的一棵最小生成树,它以(u,v)为其中一条边。
这个性质有时也称为MST性质。
3.2.Prim算法
设G=(V,E)是连通带权图,V={1,2,…,n}。
构造G的最小生成树的Prim算法的基本思想是:
首先置S={1},然后,只要S是V的真子集,就作如下的贪心选择:
选取满足条件iS,jV-S,且c[i][j]最小的边,将顶点j添加到S中。
这个过程一直进行到S=V时为止。
在这个过程中选取到的所有边恰好构成G的一棵最小生成树。
3.3.Kruskal算法
Kruskal算法构造G的最小生成树的基本思想是,首先将G的n个顶点看成n个孤立的连通分支。
将所有的边按权从小到大排序。
然后从第一条边开始,依边权递增的顺序查看每一条边,并按下述方法连接2个不同的连通分支:
当查看到第k条边(v,w)时,如果端点v和w分别是当前2个不同的连通分支T1和T2中的顶点时,就用边(v,w)将T1和T2连接成一个连通分支,然后继续查看第k+1条边;如果端点v和w在当前的同一个连通分支中,就直接再查看第k+1条边。
这个过程一直进行到只剩下一个连通分支时为止。
四、[实验代码]
1.哈夫曼编码
packageArith_Sort;
importjava.util.ArrayList;
importjava.util.List;
publicclassHoffmann{
/**
*哈夫曼算法—java实现
*/
publicstaticvoidmain(String[]args){
/*****************初始化树**********************/
int[]A={1,2,3,5};
char[]C={'a','b','c','d'};
ListTree=newArrayList();
for(inti=0;i { TreeNodetreeNode=newTreeNode(A[i],C[i]+""); Tree.add(treeNode); } /*****************初始化树**********************/ //开始建树 (newHoffmann()).buildTree(Tree,0,A.length); //获得最小生成树的根节点 TreeNodetreeNode=(TreeNode)Tree.get(Tree.size()-1); //遍历这棵树,同时给出每个字符对应的编码 (newHoffmann()).seekTree(treeNode,""); } publicvoidseekTree(TreeNodetreeNode,Stringcode) { if(treeNode! =null) { seekTree(treeNode.getLeftNode(),code+"0"); if(treeNode.getCharValue()! =null) System.out.println("字符: "+treeNode.getCharValue()+"频数: "+treeNode.getNodeValue()+"编码: "+code); seekTree(treeNode.getRightNode(),code+"1"); } } /** *建立最小生成树将两个节点组合而成的节点添置列表末尾,新一轮的排序起始位置向后移两位,自由节点数目减少1 *@paramTree *@paramstart排序列表中剩余自由节点的起始位置 *@paramfreeNodeNum列表中的自由节点数量 */ publicvoidbuildTree(ListTree,intstart,intfreeNodeNum) { if(Tree! =null&&freeNodeNum>=2) { QuickSort(Tree,start,Tree.size()-1); //取权值最小的两个节点组合成为新节点,根据排序规则,最小的两个数位于列表的前两位 TreeNodenewNode=newTreeNode(((TreeNode)Tree.get(start)).getNodeValue()+((TreeNode)Tree.get(start+1)).getNodeValue(),null); newNode.setLeftNode((TreeNode)Tree.get(start)); newNode.setRightNode((TreeNode)Tree.get(start+1)); Tree.add(newNode);//将两个节点组合而成的节点添置列表末尾 freeNodeNum--;//自由节点数减一 buildTree(Tree,start+2,freeNodeNum); } } /** *快速排序方法实现 *@paramTree *@paramp *@paramr */ publicvoidQuickSort(ListTree,intp,intr) { intq=0; if(p { q=partion(Tree,p,r); QuickSort(Tree,p,q-1); QuickSort(Tree,q+1,r); } } publicintpartion(ListTree,intp,intr) { intx=((TreeNode)Tree.get(r)).getNodeValue(); inti=p-1; for(intj=p;j<=r-1;j++) { //换个方向就是从大到小 if(((TreeNode)Tree.get(j)).getNodeValue()<=x) { i++; Objecttemp=Tree.get(i); Tree.set(i,Tree.get(j)); Tree.set(j,temp); } } Objecttemp=Tree.get(i+1); Tree.set(i+1,Tree.get(r)); Tree.set(r,temp); returni+1; } } /** *节点类 *@authors001 * */ classTreeNode{ //左节点 TreeNodeleftNode; //右节点 TreeNoderightNode; //节点值-频度 intnodeValue; StringcharValue; publicTreeNode(intnodeValue,StringcharValue) { this.nodeValue=nodeValue; this.charValue=charValue; } publicStringgetCharValue(){ returncharValue; } publicvoidsetCharValue(StringcharValue){ this.charValue=charValue; } publicTreeNodegetLeftNode(){ returnleftNode; } publicvoidsetLeftNode(TreeNodeleftNode){ this.leftNode=leftNode; } publicTreeNodegetRightNode(){ returnrightNode; } publicvoidsetRightNode(TreeNoderightNode){ this.rightNode=rightNode; } publicintgetNodeValue(){ returnnodeValue; } publicvoidsetNodeValue(intnodeValue){ this.nodeValue=nodeValue; } } 2.单源最短路径 importjava.util.Scanner; publicclassDijkstra{ privatestaticintn;//G图中的顶点个数 privatestaticint[]distent=null;//最短路径长度 privatestaticint[]previous=null;//前驱顶点集合 publicstaticvoidDijkstras(intv,int[][]a,int[]dist,int[]prev) { //单元最短路径的Dijkstra算法 intn=dist.length-1; if(v<1||v>n)return; boolean[]s=newboolean[n+1]; //初始化 for(inti=1;i<=n;i++) { dist[i]=a[v][i]; s[i]=false; if(dist[i]==Float.MAX_VALUE) prev[i]=0; else prev[i]=v; } dist[v]=0; s[v]=true; for(inti=1;i floattemp=Float.MAX_VALUE; intu=v; for(intj=1;j<=n;j++) if((! s[j])&&(dist[j] u=j; temp=dist[j]; } s[u]=true; for(intj=1;j<=n;j++) if((! s[j])&&(a[u][j] { intnewdist=dist[u]+a[u][j]; if(newdist dist[j]=newdist; prev[j]=u; }}} for(intk=2;k System.out.print(dist[k]+""); }System.out.println(dist[n]);} publicstaticvoidmain(String[]args){ //TODOAuto-generatedmethodstub intv=1; Scannersc=newScanner(System.in); System.out.println("请输入定点个数: "); Stringline=sc.nextLine(); n=Integer.parseInt(line); distent=newint[n+1]; previous=newint[n+1]; inta[][]=newint[n+1][n+1]; System.out.println("请输入顶点的权重: "); for(inti=1;i<=n;i++){ line=sc.nextLine(); String[]str=line.split(""); for(intj=1;j<=str.length;j++){ a[i][j]=Integer.parseInt(str[j-1]); }} Dijkstras(v,a,distent,previous); } } 3.最小生成树 importjava.util.Scanner; importjava.util.Arrays; importjava.util.ArrayList; publicclassPrim{ privatestaticintMAX=100; privatedoublecost[][]=newdouble[MAX][MAX]; privateArrayList privateint[]near=newint[MAX]; privatestaticdoubleINFINITY=99999999.99;//定义无穷大 privatedoublemincost=0.0;//最小成本 privateintn;//结点个数 publicPrim(){} publicstaticvoidmain(Stringargs[]){ Primsp=newPrim(); sp.init(); sp.prim(); sp.print(); } publicvoidinit(){ Scannerscan=newScanner(System.in); intp,q,w; System.out.println("spanningtreebegin! Inputthenodenumber: "); n=scan.nextInt(); //二维数组的填充要注意 for(inti=0;i Arrays.fill(cost[i],INFINITY);} System.out.println("Inputthegraph(-1,-1,-1toexit)"); while(true){p=scan.nextInt();q=scan.nextInt(); w=scan.nextInt(); if(p<0||q<0||w<0){ break; }cost[p][q]=w;cost[q][p]=w; } Edgetmp=getMinCostEdge(); edge.add(tmp);p=tmp.start; q=tmp.end;mincost=cost[p][q]; for(inti=1;i<=n;++i){ if(cost[i][p] near[i]=p; }else{ near[i]=q; } } near[p]=near[q]=0; } //寻找最小成本的边 publicEdgegetMinCostEdge(){ Edgetmp=newEdge(); doublemin=INFINITY; for(inti=1;i for(intj=i+1;j<=n;++j){ if(cost[i][j] min=cost[i][j]; tmp.start=i; tmp.end=j;}} } //System.out.println(min); returntmp; } //prim算法主体 publicvoidprim(){ //找剩下的n-2条边 for(inti=2;i doublemin=INFINITY; Edgetmp=newEdge(); for(intj=1;j<=n;++j){ if(near[j]! =0&&cost[j][near[j]] tmp.start=j; tmp.end=near[j]; min=cost[j][near[j]]; } } mincost+=cost[tmp.start][tmp.end]; edge.add(tmp); near[tmp.start]=0; for(intk=1;k<=n;++k){ if(near[k]! =0&&cost[k][near[k]]>cost[k][tmp.start]){ near[k]=tmp.start; } } } if(mincost>=INFINITY){ System.out.println("nospanningtree"); } } //打印结果 publicvoidprint(){ for(inti=0;i Edgee=edge.get(i); System.out.println("the"+(i+1)+"thedge: "+e.start+"---"+e.end); } } } classEdge { publicintstart;//始边 publicintend;//终边 } 五、[实验结果] 1.哈夫曼编码 请输入一组数,中间用空格分隔: 0.010.030.070.130.190.260.31 输出结果为: 0.01: 01000码长: 5 0.03: 01001码长: 5 0.07: 0101码长: 4 0.13: 011码长: 3 0.19: 00码长: 2 0.26: 10码长: 2 0.31: 11码长: 2 2.单源最短路径 请输入定点个数: 4 请输入顶点的权重: 24-2-4 246-1 58-35 -295-7 4-2-4 3.最小生成树 spanningtreebegin! Inputthenodenumber: 4 Inputthegraph(-1,-1,-1toexit) 1352 2351 3253 3214 -1-1-1-1 the1thedge: 2---3 the2thedge: 1---3 the3thedge: 0---0
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 贪心 算法