图论:出、入度,邻接表、邻接矩阵、拓扑排序\207. Course Schedule

转载请注明出处: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()
    {
        
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值