带权图
2016-06-16 19:52
393 查看
看了一天的带权图,终于是理解了。不过还只是理解而已。
因为本人一直追随着倪升武的博客,但是可能由于本人算法基础较差,因此在“带权图”的算法的理解上,走了好多弯路(再次证明本人的无能啊啊啊,哈哈)。
最小生成树问题
最短路径问题
下面就这2个问题分别解释一下
定义:在图中我们也有最小生成树这个概念。而在带权图中的最小生成树,指的是这棵树不仅要满足该图对应的非带权图的最小生成树的条件,还要求边的权值和最小。
算法的过程:
首先我们要假定2个容器,一个树容器,一个优先级队列的容器。我们的任务就是从放有边的队列容器中取出边放到树容器中。当树容器的顶点数目等于图的顶点的数目时,树容器内部放置的就是我们需要的最小生成树的边。而放置的过程就是我们产生最小生成树的过程。
1.选定一个顶点为源,然后将该源相关的所有Edge(成员有src:起始顶点,des:末尾顶点,distance:边的权值)添加进优先级队列容器里。当然,添加时,我们会控制每个Edge在队列中的顺序,使其按照权值来进行有序排列
2.从队列容器中找到权值最小的Edge,然后将其取出并放入树容器中。并且将我们所取的这个Edge对象的des顶点设置为当前节点。
3.因为第二步中当前顶点改变了,因此我们再重复第1步和第2步的操作,直至树容器内部Edge的数目=图的顶点数目-1。
以上就是这个算法的基本过程。下面是取自倪升武的博客的代码。我会在其基础上再详细的添加一些注释帮助大家理解。
简要说一下上面的工作流程:就是选定一个源,根据优先级队列有序排列这个特点,选取队列末尾这个权值最小的Edge加入树容器,再将该Edge的des顶点选定为新的源,重复上述的过程,直至得到最小生成树。
实现最短路径的算法有很多,比如Dijkstra算法,(迪杰斯特拉),Floyd(弗洛伊德)算法等。这里我们仅介绍Dijkstra算法。
建议往下阅读之前,首先阅读绿岩的这篇博客,尤其是里面的框图,我认为值得一读。
下面是倪升武的代码,如果不理解Dijkstra算法过程,下面的代码我估计你是不容易读明白的(大神请无视)。
另外,为了分析方便,下面的代码有些量未完全引入,完整代码请移步倪升武的博客
因为本人一直追随着倪升武的博客,但是可能由于本人算法基础较差,因此在“带权图”的算法的理解上,走了好多弯路(再次证明本人的无能啊啊啊,哈哈)。
带权图
在图定义的基础上,然后将每个边加上一定的权重,这就构成了带权图。其实在带权图这部分,重要的是其2个重要的问题:最小生成树问题
最短路径问题
下面就这2个问题分别解释一下
最小生成树问题
注意:下面我们讨论的图都是连通图。定义:在图中我们也有最小生成树这个概念。而在带权图中的最小生成树,指的是这棵树不仅要满足该图对应的非带权图的最小生成树的条件,还要求边的权值和最小。
算法的过程:
首先我们要假定2个容器,一个树容器,一个优先级队列的容器。我们的任务就是从放有边的队列容器中取出边放到树容器中。当树容器的顶点数目等于图的顶点的数目时,树容器内部放置的就是我们需要的最小生成树的边。而放置的过程就是我们产生最小生成树的过程。
1.选定一个顶点为源,然后将该源相关的所有Edge(成员有src:起始顶点,des:末尾顶点,distance:边的权值)添加进优先级队列容器里。当然,添加时,我们会控制每个Edge在队列中的顺序,使其按照权值来进行有序排列
2.从队列容器中找到权值最小的Edge,然后将其取出并放入树容器中。并且将我们所取的这个Edge对象的des顶点设置为当前节点。
3.因为第二步中当前顶点改变了,因此我们再重复第1步和第2步的操作,直至树容器内部Edge的数目=图的顶点数目-1。
以上就是这个算法的基本过程。下面是取自倪升武的博客的代码。我会在其基础上再详细的添加一些注释帮助大家理解。
//边界路径类,主要记录了边的始末顶点,以及边的权值 class Edge { public int srcVert; //index of vertex starting edge public int destVert; //index of vertex ending edge public int distance; //distance from src to dest public Edge(int sv, int dv, int d) { srcVert = sv; destVert = dv; distance = d; } } //自定义优先队列,用来存储边 class PriorityQ { private final int SIZE = 20; private Edge[] queArray; //存储边界的数组 private int size; public PriorityQ() { queArray = new Edge[SIZE]; size = 0; } //从这个方法我们可以看出,queArray这个优先级队列是按照权值从大到小的顺序排列的。队列的最后是权值最小的Edge。 public void insert(Edge item) { //有序的插入边界 int j; for(j = 0; j < size; j++) { //找到插入的位置,从0到size-1,逐渐减小 if(item.distance >= queArray[j].distance) break; } //比item.distance小的往后挪一位,给腾出个空间 for(int k = size-1; k >= j; k--) { queArray[k+1] = queArray[k]; } queArray[j] = item; //插入item size++; } public Edge removeMin() { //删除最小的边界并返回 //这里的‘删除’,是将标志队列数目的size减1,这样最后一个元素的索引值就不在我们的使用范围之内了。也就达到了删除的效果。下面这行注释的代码是我自己添加的,可以有助于大家的理解。 //queArray[size-1] = 0; return queArray[--size]; } public void removeN(int n) { //删除n位置的边界 for(int j = n; j < size-1; j++) { queArray[j] = queArray[j+1]; } size--; } public Edge peekMin() { //返回最小边界,不删除 return queArray[size-1]; } public Edge peekN(int n) { //返回n位置的边界 return queArray ; } public int size() { return size; } public boolean isEmpty() { return (size == 0); } public int find(int findDex) { //寻找特定disVert的边界索引 for(int j = 0; j < size; j++) { if(queArray[j].destVert == findDex) return j; } return -1; } } //带权图类 public class WeightedGraph { private final int MAX_VERTS = 20; //最大顶点数 private final int INFINITY = 100000; //最远距离...表示无法达到 private Vertex[] vertexArray; //存储顶点的数组 private int adjMat[][]; //存储顶点之间的边界 private int nVerts; //顶点数量 private int currentVert; //当前顶点索引 private PriorityQ thePQ; //存储边的优先级队列 private int nTree; //最小生成树中的顶点数量 public WeightedGraph() { vertexArray = new Vertex[MAX_VERTS]; adjMat = new int[MAX_VERTS][MAX_VERTS]; for(int i = 0; i < MAX_VERTS; i++) { for(int j = 0; j < MAX_VERTS; j++) { adjMat[i][j] = INFINITY; //初始化所有边界无穷远 } } thePQ = new PriorityQ(); } public void addVertex(char lab) { //添加顶点 vertexArray[nVerts++] = new Vertex(lab); } public void addEdge(int start, int end, int weight) {//添加带权边 adjMat[start][end] = weight; adjMat[end][start] = weight; } public void displayVertex(int v) { System.out.print(vertexArray[v].label); } /* * 带权图的最小生成树,要选择一条最优的路径 */ public void MinSpanningTree() { currentVert = 0; //从0开始 while(nTree < nVerts-1) { //当不是所有节点都在最小生成树中时 //isInTree是上一节Vertex类中新添加的成员变量 private boolean isInTree; //表示有没有加入到树中,初始化为false vertexArray[currentVert].isInTree = true; //将当前顶点加到树中 nTree++; //往PQ中插入与当前顶点相邻的一些边界 for(int i = 0; i < nVerts; i++) { if(i == currentVert) //如果是本顶点,跳出 continue; if(vertexArray[i].isInTree) //如果顶点i已经在树中,跳出 continue; int distance = adjMat[currentVert][i]; //计算当前顶点到i顶点的距离 if(distance == INFINITY) continue; //如果当前顶点与i顶点无穷远,跳出 putInPQ(i, distance); //将i节点加入PQ中 } if(thePQ.size() == 0) { //如果PQ为空,表示图不连接 System.out.println("Graph not connected!"); return; } Edge theEdge = thePQ.removeMin(); int sourceVert = theEdge.srcVert; currentVert = theEdge.destVert; System.out.print(vertexArray[sourceVert].label);//这里就是一步步打印最小生成树的路径 System.out.print(vertexArray[currentVert].label); System.out.print(" "); } } //这个方法是将一个Edge放入优先级队列,保证队列中每个Edge的des顶点是不同的。 private void putInPQ(int newVert, int newDist) { int queueIndex = thePQ.find(newVert);//判断PQ中是否已经有到相同目的顶点的边界 if(queueIndex != -1) { //如果有则与当前顶点到目的顶点的距离作比较,保留短的那个 Edge tempEdge = thePQ.peekN(queueIndex);//get edge int oldDist = tempEdge.distance; if(oldDist > newDist) { //如果新的边界更短 thePQ.removeN(queueIndex); //删除旧边界 Edge theEdge = new Edge(currentVert, newVert, newDist); thePQ.insert(theEdge); } } else { //如果PQ中没有到相同目的顶点的边界 Edge theEdge = new Edge(currentVert, newVert, newDist); thePQ.insert(theEdge);//直接添加到PQ } } }
简要说一下上面的工作流程:就是选定一个源,根据优先级队列有序排列这个特点,选取队列末尾这个权值最小的Edge加入树容器,再将该Edge的des顶点选定为新的源,重复上述的过程,直至得到最小生成树。
最短路径问题
所谓的最短路径,即2点之间的路径要求权值最小。实现最短路径的算法有很多,比如Dijkstra算法,(迪杰斯特拉),Floyd(弗洛伊德)算法等。这里我们仅介绍Dijkstra算法。
建议往下阅读之前,首先阅读绿岩的这篇博客,尤其是里面的框图,我认为值得一读。
下面是倪升武的代码,如果不理解Dijkstra算法过程,下面的代码我估计你是不容易读明白的(大神请无视)。
另外,为了分析方便,下面的代码有些量未完全引入,完整代码请移步倪升武的博客
/************************** 最短路径问题 ****************************/ /** * path()方法执行真正的最短路径算法。 */ public void path() { //寻找所有最短路径 /* * 源点总在vertexArray[]数组下标为0的位置,path()方法的第一个任务就是把这个顶点放入树中。 * 算法执行过程中,将会把其他顶点也逐一放入树中。把顶点放入树中的操作是设置一下标志位即可。 * 并把nTree变量增1,这个变量记录了树中有多少个顶点。 */ int startTree = 0; //从vertex 0开始 vertexArray[startTree].isInTree = true; nTree = 1; /* * path()方法把邻接矩阵的对应行表达的距离复制到sPath[]中,实际总是先从第0行复制 * 为了简单,假定源点的下标总为0。最开始,所有sPath[]数组中的父节点字段为A,即源点。 */ for(int i = 0; i < nVerts; i++) { int tempDist = adjMat[startTree][i]; //sPath中保存的都是到初始顶点的距离,所以父顶点默认都是初始顶点,后面程序中会将其修改 sPath[i] = new DistPar(startTree, tempDist); } /* * 现在进入主循环,等到所有的顶点都放入树中,这个循环就结束,这个循环有三个基本动作: * 1. 选择sPath[]数组中的最小距离 * 2. 把对应的顶点(这个最小距离所在列的题头)放入树中,这个顶点变成“当前顶点”currentVert * 3. 根据currentVert的变化,更新所有的sPath[]数组内容 */ while(nTree < nVerts) { //1. 选择sPath[]数组中的最小距离 int indexMin = getMin(); //获得sPath中的最小路径值索引 int minDist = sPath[indexMin].distance; //获得最小路径 if(minDist == INFINITY) { System.out.println("There are unreachable vertices"); break; } //2. 把对应的顶点(这个最小距离所在列的题头)放入树中,这个顶点变成“当前顶点”currentVert else { //reset currentVert currentVert = indexMin; startToCurrent = sPath[indexMin].distance; } vertexArray[currentVert].isInTree = true; nTree++; //3. 根据currentVert的变化,更新所有的sPath[]数组内容 adjust_sPath(); } displayPaths(); nTree = 0; for(int i = 0; i < nVerts; i++) { vertexArray[i].isInTree = false; } } //获取sPath中最小路径的索引 private int getMin() { int minDist = INFINITY; int indexMin = 0; for(int i = 0; i < nVerts; i++) { if(!vertexArray[i].isInTree && sPath[i].distance < minDist) { minDist = sPath[i].distance; indexMin = i; } } return indexMin; } /*调整sPath中存储的对象的值,即顶点到初始顶点的距离,和顶点的父顶点 * 这是Dijkstra算法的核心 */ private void adjust_sPath() { int column = 1; while(column < nVerts) { if(vertexArray[column].isInTree) { column++; continue; } int currentToFringe = adjMat[currentVert][column]; //获得当前顶点到其他顶点的距离,其他顶点不满足isInTree int startToFringe = startToCurrent + currentToFringe; //计算其他顶点到初始顶点的距离=当前顶点到初始顶点距离+当前顶点到其他顶点的距离 int sPathDist = sPath[column].distance; //获得column处顶点到起始顶点的距离,如果不与初始顶点相邻,默认值都是无穷大 if(startToFringe < sPathDist) { sPath[column].parentVert = currentVert; //修改其父顶点 sPath[column].distance = startToFringe; //以及到初始顶点的距离 } column++; } } //显示路径 private void displayPaths() { for(int i = 0; i < nVerts; i++) { System.out.print(vertexArray[i].label + "="); if(sPath[i].distance == INFINITY) System.out.print("infinity"); else System.out.print(sPath[i].distance); char parent = vertexArray[sPath[i].parentVert].label; System.out.print("(" + parent + ") "); } System.out.println(""); }
相关文章推荐
- CTF【每日一题20160615】
- uitextfiled uitextview过滤emoji符号 ios
- Java反射机制
- ubuntu安装redis
- Java中把JSON和List结果集互转的代码片段整理
- IPC基础概念Serializable、Parcelable、Binder
- Java中把JSON和List结果集互转的代码片段整理
- 变态跳台阶
- OVS-DPDK VM出现 virtio_net virtio4: output.0:id 30 is not a head 问题 dpdk-vhost bug fix
- 点击cell动态改变cell高度
- Java Web项目--显示一个静态页面
- 原型继承+原型链 + 对象继承发展
- PHP微信API接口页面代码
- 2、Ubuntu 下启动 nfs 服务
- how to open ubuntu port like 8443
- 外部系统xml数据发送至NC
- X5、WebViewJavascriptBridge、百度地图网页版 整合注意事项
- 文章标题
- MySQL基础教程(存储过程) 常用函数详解(日期函数、时间函数和字符串函数)
- ISLR读书笔记(1)统计学习简介