转载请注明出处:http://blog.csdn.net/c602273091/article/details/55511145
出入度
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
各种图的概念请看:【6】
无向边:若顶点Vi到Vj之间的边没有方向,则称这条边为无向边(Edge),用无序偶(Vi,Vj)来表示。
有向边:若从顶点Vi到Vj的边有方向,则称这条边为有向边,也成为弧(Arc),用有序偶< Vi,Vj>来表示,Vi称为弧尾,Vj称为弧头。
简单图:在图结构中,若不存在顶点到其自身的边,且同一条边不重复出现,则称这样的图为简单图。
无向完全图:在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。含有n个顶点的无向完全图有n*(n-1)/2条边。
有向完全图:在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则称该图为有向完全图。含有n个顶点的有向完全图有n*(n-1)条边。
稀疏图和稠密图:这里的稀疏和稠密是模糊的概念,都是相对而言的,通常认为边或弧数小于n*logn(n是顶点的个数)的图称为稀疏图,反之称为稠密图。
有些图的边或弧带有与它相关的数字,这种与图的边或弧相关的数叫做权(Weight),带权的图通常称为网(Network)。
然后介绍度,以顶点V为头的弧的数目称为V的入度(InDegree),记为ID(V),以V为尾的弧的数目称为V的出度(OutDegree),记为OD(V),因此顶点V的度为TD(V)=ID(V)+OD(V)。一般指的是在有向图(DAG)中,某个顶点,箭头指向它的为入度,从这个顶点出发,指向别的顶点的边就是出度。有几条这样的边,度就是多大。看【7】中的图有详细的介绍。
如果一个有向图恰有一个顶点入度为0,其余顶点的入度均为1,则是一棵有向树。
可以参考【1】中生成出入度的代码。不过【1】中是认为无向图中一条边既是入度、也是出度。所以这里的计算会有些许不同。
邻接表、邻接矩阵
邻接表这个东西也可以看【1】中是怎么写这个邻接矩阵的,我更推荐的是看【2】中鱼c的文章。写得非常好。
其实邻接表和邻接矩阵是离散数学的必修内容,现在就是对它进行一个复习。
首先邻接矩阵的纵轴坐标就是各个边的初始顶点,横坐标就是各个边的箭头的的位置。如果存在i->j这条边,那么就使矩阵M(i, j) = 1,否则为0。
邻接表是另外一种记录图的数据结构。因为图是稀疏的话,那么邻接矩阵就会使得存储很浪费,使用邻接表存储更加节约空间。如果图是稠密的话,使用邻接表是一种不错的方式。
在这里强烈推荐这个博客,写得非常好,非常简单易懂【3】【8】。
在图的存储结构中,还有十字链表、邻接多重表、边集数组。
边集数组:边集数组是由两个一维数组构成,一个是存储顶点的信息,另一个是存储边的信息,这个边数组每个数据元素由一条边的起点下标(begin)、终点下标(end)和权(weight)组成【9】。
另外在【11】中,对图的表示举了比较简单的例子。
拓扑排序
拓扑排序我觉得就是一个有向无环图的问题。有向无环这就是拓扑图的充要条件。
在计算拓扑图方面,有DFS和Kahn算法。这里我主要是参考了【4】
DFS
从wiki上获取它的伪代码为:
L ← Empty list that will contain the sorted nodes
S ← Set of all nodes with no outgoing edges
for each node n in S do
visit(n)
function visit(node n)
if n has not been visited yet then
mark n as visited
for each node m with an edge from m to n do
visit(m)
add n to L
具体的代码实现为:
public class DirectedDepthFirstOrder
{
// visited数组,DFS实现需要用到
private boolean[] visited;
// 使用栈来保存最后的结果
private Stack<Integer> reversePost;
/**
* Topological Sorting Constructor
*/
public DirectedDepthFirstOrder(Digraph di, boolean detectCycle)
{
// 这里的DirectedDepthFirstCycleDetection是一个用于检测有向图中是否存在环路的类
DirectedDepthFirstCycleDetection detect = new DirectedDepthFirstCycleDetection(
di);
if (detectCycle && detect.hasCycle())
throw new IllegalArgumentException("Has cycle");
this.visited = new boolean[di.getV()];
this.reversePost = new Stack<Integer>();
for (int i = 0; i < di.getV(); i++)
{
if (!visited[i])
{
dfs(di, i);
}
}
}
private void dfs(Digraph di, int v)
{
visited[v] = true;
for (int w : di.adj(v))
{
if (!visited[w])
{
dfs(di, w);
}
}
// 在即将退出dfs方法的时候,将当前顶点添加到结果集中
reversePost.push(v);
}
public Iterable<Integer> getReversePost()
{