图的算法复习大纲

一、无向图

  1.无向图的实现

  2.深度优先搜索检查是否可达

  3.深度优先搜索获取可达路径

  4.广度优先搜索获得最短路径(只要塞入队列就标记,塞入就算是访问了)

  5.深度优先搜索获得图的连通分量数目和各个顶点所在的连通子图id

  6.深度优先搜索判断图是否有环,判断图是否用2种颜色图且相邻顶点不同色(二分图),二分图的一个实例是:电影是顶点,演员也是顶点,他们构成的图就是二分图,这个二分图一开始是根据电影对应哪些演员生成的,但最后却可以得到某个演员演了哪些电影,即二分图有天生的反向索引功能。不过实现上需要符号图的帮助,不然你咋知道1,2那个是电影

  7.符号图的实现

  8. 欧拉路径和欧拉环

    无向图G存在欧拉路径的充要条件:图G是连通的,且至多除两个点外(可以为0个,连接图不可能有且仅有一个顶点的度奇数)其它所有顶点的度为偶数.要找欧拉路径, 满足上述条件,只要简单的找出一个度为奇数的节点,遍历结点,看是否图连通。

    无向图,每个顶点的度数都是偶数,则存在欧拉环;有向图,每个节顶点的入度都等于出度,则存在欧拉环。

二、有向图

  1.有向图的实现

  2.深度优先搜索的可达性和路径

  3.广度优先搜索的可达性和路径

  4.有向环的识别:有向图中是否有有向环

  5.基于深度优先搜索的顶点排序,在访问的dfs最后,把当前访问元素压入堆栈。栈顶到栈底就是我们要的顺序,用于拓扑排序。(也可以用队列来,在dfs开始那里压入队列)

  6.对无环的有向图的顶点进行拓扑排序(后者的入度来源总是在前者中):先识别无环,然后基于深度优先搜索的顶点排序的到的就是了。

  7.强连通性:图的任意2个顶点间都互相可达

    1.强连通性把图的所有顶点分割为几个子集,这些子集成为强连通分量,强连通分量内部一般关系密切,在实际处理数据时,一般先得到图的强连通分量,然后进行处理。

    2.Kosaraju算法得到强连通分量:只在无向图的连通分量那里加上一点:在构造函数中,按某种顺序访问顶点,并对其进行dfs即可,只是访问顺序和无向图不同:

      先用第5点的深度优先搜索得到“图的逆向图”的顶点访问顺序(栈S),然后对这个顺序访问即可。书里的证明写得很好但核心部分不如我下面写得好,认真看能看懂。

      在G中访问顺序是s->v,即栈S中,s在顶v在底,所以在G的逆的深度优先搜索中,先v入栈,然后s才入栈,这有2种可能:

      1.dfs(v)在dfs(s)前就结束了,2.dfs(v)在dfs(s)内部。第1种不可能,因为在G的逆中,v->s可达,所以只能是2,那么在G的逆中,s->v可达,即在G中v->s可达,证毕。

  8.任意顶点对的可达性,目前还在研究,对小图而言,可以给每个顶点都生成一条到其他顶点的路径,用深度/广度优先搜索的可达性解决。

三、最小生成树

  1.生成树:图的生成树是它的一棵含有其所有顶点的无环连通子图。最小生成树是它的一棵权重最小的生成树。

  2.切分定理和贪心算法:

    1.图的切分:图的一种切分是将图的所有顶点分为2个非空且不重复的集合,横切边是一条连接属于2个不同集合的顶点的边

    2.切分定理:在一幅加权图中,给定任意切分,它的横切边中的权重最小者必然属于图的最小生成树。

    3.最小生成树的贪心算法:对于V个顶点的加权连通图,初始化所有边为灰色,找到一种切分,它的横切边都不是黑色,则将它权重最小的横切边标为黑色。反复,知道标记了V-1条黑色边为止。证明:因为V个顶点的树有V-1条边,又有切分定理,证毕。

  3.加权无向图的实现

  4.Prim算法

    1.Prim算法的延时实现,队列存的是边

    2.Prim算法的即时实现,队列存的是顶点和它到最小生成树的最小距离,另外每个顶点都在更新距离的同时更新其到树的父亲。

  5.Kruskal算法:每次都取最小权重的边,然后如果不和当前最小生成树构成环,则加入最小生成树中,对其原理不甚明了,但其按权重最小顺序得到横切边可能有特殊应用。

四、最短路径:

  1.加权有向图的实现

  2.理论基础:

    1.定义:在一幅加权有向图中,从顶点S到T的最短路径是所有从S到T的路径中的权重最小者。

    2.用int[] disTo[]来存各个顶点到S点的最短距离(初始化时,disTo[S]=0,其他的为Int.MaxValue)

    3.边的放松:

      DirectedEdge e; int v = e.from(); int w = e.to();

      if (disTo[w] > disTo[v] + e.weight()) { disTo[w] = disTo[v] + e.weight(); edgTo[w] = e;}

    4.顶点的放松:对v所有边进行放松

    5.最短路径最优条件:当所有边都不能再放松时(即所有顶点disTo[w] <= disTo[v] + v.weight(), w为任意顶点,v为任意可达w的相邻顶点),我们就得到最短路径了。

    6.所以最短路径的目标是:找到遍历所有顶点的方法,使得遍历后的顶点的disTo[v]都不会再变小。

  3.Dijkstra算法:

      把第一个顶点加入索引优先队列,接着把队列里disTo最小的顶点拉出来放松,并更新队列中disTo变化/新增的顶点,反复,直到队列为空。很类似Prim的即时版

      这里要理解一点:放松后的顶点的disTo不会再变小了。设访问v之前所有放松过的顶点的集合为A,因为队列都是先取距离集合A最小的顶点放松,那么v到达这个集合的距离肯定比k小(k是任意比v晚放松的顶点),又从k到v的权重只能是正的,所以disTo[v]不会因为k而变小,即放松v后disTo[v]就不会改变了,满足我们的目标。

      Dijkstra算法能解决加权有向图的最短路径问题(非负权重),图可以有环。

  4.无环加权有向图的最短路径和最长路径(权重可正可负):

      1.我们学有向图时知道无环有向图的顶点拓扑排序满足:后面顶点不会是前面顶点的入度(或者是依赖),这意味着,如果我们按照拓扑排序的顺序放松各个顶点,那每个放松过的顶点的disTo[v]都不会再变小了。这个满足我们的目标。这种方法的图必须无环,但权重可以为负,实现上也不难。

      2.同1一样,我们可以按拓扑排序放松各个顶点,但disTo[]的初始值为int.MinValue并且放松函数里面改变不等号方向,这样就可以得到最长路径了;或者使用另外的方法,先拷贝一份图,并把各个边的权重加个负号,然后依然按拓扑排序放松各个顶点,这样照样可以得到最长路径。

      3..有优先级限制的任务调度:

        首先,我们要构建一个图。

        第一步,添加一个虚拟的起点和一个虚拟的终点;

        第二步,对每个一定耗时的任务:抽象为2个顶点,添加连接2顶点的边,权重是该任务的耗时,接着添加图起点到顶点起点的边但权重为0,添加顶点终点到图终点的边但权重为0;

        第三步,对每个任务的优先级限制(必须先A才能B,即A->B),添加连接A任务终点顶点到B任务起点的边,权重为0;

      构建图完毕, 求其最长路径,得到的disTo[图终点]就是在允许任意多个处理线程的情况下处理全部任务的最短耗时了(如果是单线程,只能按拓扑排序那样搞一波,如果是n个线程呢?初步想法是还是按拓扑的来,每个线程处理一个队列:遍历所有任务,当任务的依赖任务处于某个线程中时,把该任务加入该线程,否则,如果有空闲线程就给该线程,如果木有就给当前任务耗时最小的线程,这样应该算比较不错的统筹方法了)。扯远了,下面要证明一下这个最长路径为何就能得到最短耗时。

      任何一个任务,其开启依赖于各个指向它的任务,即依赖多条从起点到它那里的路径,必须等所有路径上的任务都完成了才能开启它,又因为有多个处理线程,所有最长路径就成了关键点,即处理到达它的最长路径的那个处理线程处理到它那里后,其他路径的线程都完成了,这个时候就处理它,那那个处理最长路径的线程可以让出一部分任务让其他线程分担吗?不能,因为路径上的任务都依赖其前置任务。故,图的终点的disTo就是最终耗时。

  5.一般加权有向图的最短路径算法:Bellman-Ford算法,这个我懒得看了。=。=

总结,图的内容还是很不错的,无论是理论研究还是应用,复习时记得边看大纲边想,边联系书和实现。多看几遍就好了,并且会越来越快的。

原文地址:https://www.cnblogs.com/Tearix/p/7580446.html