对于语义的分析,我们采用的控制流和数据流也都无一例外的采用了以图为基础的算法,通过图的可达性,来完成变量、表达式的可达分析,以及变量的依赖分析、值流图等等。
图的算法是进行静态分析的基础数据算法,如何提高图的分析效率,就需要对图的算法有进一步的认识。
1986年的图灵奖是JohnE.Hoperoft和RobertE·Tarjan两人共同获得,而且RobertE·Tarjan曾是JohnE.Hoperoft的学生,他们的密切合作取得了算法设计与分析方面的卓越贡献,赢得了1986年度美国计算机协会(AssociationforComputingMachinery,ACM)图灵奖的荣誉。
在领奖的时候,JohnE.Hopcroft做了一个“计算机科学:一门学科的出现”的演讲,从他自己的经历出发,展现了计算科学在那个风起云涌的年代形成的过程。
那个时候,计算机科学的大部分课程的内容都是围绕着数字计算机电路的设计以及如何减少构造这些电路所需要的晶体管数展开的。然而,到了六十年代中期,技术的进步已使得晶体管马上就要被每片有多达几百个元件的计算机芯片所取代。因此,减少晶体管数再没有么意义,那时所谓的计算机科学的课程即将过时,必须发展新的课程。
Hopcroft并没有接受过正规计算机教育,只是他在斯坦福大学读电器工程学博士的时候,上过DavidHuffman(霍夫曼编码的发明者)教的一门计算机课程,学习了开关电路、逻辑设计以及计算理论的基本知识。普林斯顿要Hoperoft在自动机理论方面开设一门新的课程,当时没有任何教程可以参考,只给了他推荐了6篇论文。其中包括:
1967年,Hopcroft转去康奈尔大学,转而研究算法与数据结构。他相信理论计算机科学的方法学,可以用来为算法设计发展一种科学基础,这对于实践者将是很有用的。
Hopcroft在发言的最后,还不忘呼吁,为了保持美国取得的技术和经济的领导地位,必须对计算机科学进行全国性的投入和支持。
说到Tarjan,他在图论算法和数据结构领域有很大的贡献。下面对这个大牛也做个简单的介绍。
Tarjan在1969年获得了加州理工学院数学学士学位。在斯坦福大学,他获得了他的计算机科学硕士学位(1971)和博士学位(1972).Tarjan从1985年开始任教于普林斯顿大学。
Tarjan还拥有很多研究所和公司的工作经验,例如:AT&T贝尔实验室、NEC研究所、InterTrustTechnologies、康柏、惠普、微软等。
Tarjan是一个非常高产的计算机科学家,从计算机科学出版物在线参考网站dblp收集的有关他发表在的杂志、会议和出版物,多达300+。
他独立研究的算法有:Tarjan离线的LCA算法(一种优秀的求最近公共祖先的线性离线算法)、Tarjan强连通分量算法(甚至比后来才发表的Kosaraju算法平均要快30%)、Hopcroft-Tarjan算法(第一个平面性测试的线性算法)。
他还开发了一些重要的数据结构,比如斐波那契堆(FibonacciHeap,插入查询合并O(1),删除O(logn)的强大数据结构)、伸展树(SplayTree,和另外一位计算机科学家共同发明)、动态树(Link-CutTree,发明人之一)。
下图是他普林斯顿大学个人简介中的一个插图,这个是他的另一个重要的研究领域:自适应搜索树的查询(self-adjustingsearchtree),在树的遍历过程中,改变树的结构以提高树的搜索效率。
他的主要荣誉:
图的一些基本概念:
Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
oLOW(u)为u或u的子树能够追溯到的最早的栈中节点的次序号;由定义可以得出,当DFN(u)=LOW(u)时,以u为根的搜索子树上所有节点是一个强连通分量。
1)如果v不在栈中(树枝边),DFS(v),并且LOW[u]=min{LOW(u),LOW(v)};
2)如果v在栈中(前向边/后向边),此时LOW[u]=min{LOW[u],DFN[v]}
0.求下面有向图的强连通分量
1.从节点0开始DFS:
2.返回节点4:
3.返回节点2:
4.从节点2继续搜索到节点3:
5.返回节点2;
6.返回节点0;
7.从节点0继续搜索到节点1;
8.返回节点0;
为了便于后期的扩展,适用更为广泛的图运算。这里算法的实现中,图的表示采用了Googleguava库中的common.graph,你需要在pom.xml中加入guava的依赖包如下:
为了和举例中图的特征保持一致,图结构采用guava里的MutableGraphgraph,Integer的值表示结点的编号。例如graph.putEdge(0,2);表示增加一条有向边,从结点0指向结点2,graph会判断结点0和2是否存在结点中存在,如不存在,则会增加相应的结点。
大家可以根据自己的需要采用不同的图结构。
验证代码使用junit完成结果的验证。生成可变图graph里面的:incidentEdgeOrder(ElementOrder.stable()),是为了保证关联边的读取时和输入的顺序一致,以便得到和前面演算过程的一致性的结果.