图论复习笔记

补充小细节:
a. 什么样的图可以称为“稀疏”:|E|=O(|V|),也就是说平均每个顶点的邻接顶点数量是常数;

1. 基本定义

图(Graph)的定义:G=(V,E)由顶点(vertex)集V和边(edge)集E组成。
边(弧arc)是一个点对(v, w),其中v,w∈V;
有向:点对是有序的,那么图有就有向的;
邻接:v和w邻接当且仅当(v,w)∈E,而(w,v)是指w和v邻接;
简单路径:其上的所有顶点都是互异的,但第一个顶点和最后一个顶点可能相同;
:一条从一个顶点到其自身的边(v,v)那么路径v,v有时候也叫做一个环;
:有向图中的圈满足w1 = wn;且长至少为1的1条路径;圈也可以是简单路径,即简单圈;
有向无圈图(DAG)

图的连通性
无向图中每个顶点到其他路径都存在一个条路径,该无向图是连通的;
有向图中~,该有向图是强连通的;
有向图不是强连通的,但它的基础图(underlying graph去掉方向后的无向图)是连通的,则它是弱连通的;
完全图:是每对顶点间都存在一条边;

图的表示
邻接矩阵(空间需求O(|V|^2))和邻接表(adjacency list,空间:O(|E| + |V|));
邻接表是表示图的标准方法,对于无向图,每条边(u,v)出现在两个表中,空间因此是双倍的;

2. 拓扑排序(For 有向无圈图):

利用队列存放入度为0的顶点来遍历,过程类似层序遍历:

void Topsort( GRAPH G) {
        size_t counter = 0;
        Queue Q = CreateQueue(G-> NumVertex); //创建队列
       MakeEmpty(Q);
        Vertex v, w;

        for(size_t i = 0; i < G->NumVertex; i++) { //将入度为0的顶点首先放入队列
              v = G-> VERTEXS[i];
               printf("入度:%d\n" , v->InDegree );
               if(InDegree(v) == 0) {
                     EnQueue(v, Q);
              }
       }

        while(!isEmpty(Q)) {
              v = ( Vertex) DeQueue(Q);
              counter++;
               printf("第%d个顶点是:%d\n" , counter, v->Element);
               Node d = v-> list;
               while(d != NULL) {
                     w = d-> v;
                     w-> InDegree--;
                      if(InDegree(w) == 0)
                           EnQueue(w, Q);
                     d = d-> next;
              }
       }

        if(counter != G->NumVertex ) {
              println( "这是一个有圈图!" );
       }
}

3. 最短路径算法:

最短路径求解的问题,需要根据图的情况而定:
单源最短路径问题:给定一个赋权图G = (V, E)和一个特定顶点s作为输入,找出s到G中每一个其他顶点的最短赋权路径;
这个问题有4种形态:
无权图【无权最短路径O(|V| + |E|)】;
有向赋权图/无负边【赋权最短路径O(|E|log|V|)】;
有向赋权图/有负边【O(|E|*|V|)】;
无圈图【O(|E| + |V|)】;

3.1 无权最短路径
/**
 * 无权最短路径计算
 */
void Unweighted( GRAPH G, Vertex s) {
        Queue Q = CreateQueue(7);
       MakeEmpty(Q);
        Vertex v,w;
        Info *infos = CreateVertexInfo(G-> NumVertex);
       EnQueue(s, Q);
       infos[s-> Element - 1]-> Distance = 0;
        while(!isEmpty(Q)) {
              v = DeQueue(Q);
              infos[v-> Element - 1]-> Known = 1; //设为已知顶点
               Node d = v-> list;
               while(d != NULL) { //开始遍历检查与v邻接的顶点
                     w = d-> v;
                      if(infos[w->Element - 1]->Distance == INT_MAX) { //这样可以保证每个顶点只算最先计算的一次,因为是无权的
                           infos[w-> Element - 1]-> Distance = infos[v-> Element - 1]->Distance + 1;
                           infos[w-> Element - 1]-> PreVertex = v->Element ; //设置前序顶点
                           EnQueue(w, Q);
                     }
                     d = d-> next;
              }
       }
       PrintVertexInfo(infos, G-> NumVertex);
       DisposeQueue(Q);
       DisposeVertexInfo(infos);
}
3.2 赋权最短路径计算

这里使用了贪婪算法,每轮需要确定一个当前“最近”的顶点设为“已知”;在迭代的过程中每一轮都需要输出一个最小值,这样的话维护一个优先队列(堆)是一个很好的选择;
好的做法是:维护一个包含“未知”节点的(最小)堆,在每轮开始的时候输出堆顶,更新时修改顶点的distance值;这两个过程的时间复杂度都是O(log|V|);总的时间复杂度是,O(|E|log|V| + |V|log|E|);

可选的优先级队列的实现有:二叉堆,配对堆和斐波那契堆等;

3.3 带有负边的图:

如果负边,那么Dijkstra算法就不可行,因为可能不存在“最短”的路径【负值圈】;
如果考虑每条边的权+常数k使得每条边的权都为正数,可以将负边问题去掉,但是这样边数多的路径比边数少的路径更重了,这也可能导致错误;
解决办法是将赋权和无权结合起来;

3.4 无圈图:

无圈图可以很容易在每一阶段确定这一层的顶点的最短路径,因为不存在其他可能更小的情况;因此不需要优先级队列,时间复杂度/过程与无权图和拓扑排序类似:O(|V| + |E|);

关键路径分析法->动作节点图->事件节点图:
最早完成时间:EC1 = 0; ECw = max(ECv + Cv,w);
最晚时间:LCn = ECn; LCv = min(LCw-Cv,w);【倒转上一个问题的拓扑结构】
松弛时间:Slack(v,w) = LCw - ECv - Cv,w;

关键路径是【零-松弛边组成的路径】

3.5 所有点对的最短路径(路由、拓扑):【注意是点对】

对于稀疏图更快的是运行|V|次用优先队列编写的Dijkstra算法;

4. 网络流问题:

4.1 基本概念:

发点【source】;
收点【sink】;
发点和收点之间的最大流问题;
残余图【residual graph】Gr:表示每条边还能再添加多少流,可以从容量中减去当前的容量得到残余的流量;
残余边【residual edge】:Gr的边称为残余边;
增长通路(augmenting path):寻找图Gr中的s到t的一条路径,这条路径就叫做增长通路;

4.2 最大流算法

合理做法是:每轮(这轮流量为流f(v,w))在残余图中增添一条容量为f(v,w)的边(w,v);
终止条件:Gr中从t从s出发是不可到达的;

为了防止在迭代选择增长通路的时候,优先甚至反复选择较小流量的路径,有如下两种方法:
a. 总选择使得流增长最大的增长通路(O(|E|^2log|V|logcapmax));
b. 总选择具有最少边数的路径(O(|E|^2|V|));

5. 最小生成树(边数为|V|-1)

5.1 Prim算法(类似Dijsktra算法,使用|V|优先队列维护顶点三元组信息):
5.2 Kruskal算法(|E|大小的优先队列,不保存顶点三元组信息,维护顶点集合,不断合并顶点集合,直到成为一个集合):

6. 深度优先搜索(线性时间解决,O(|E| + |V|))

6.1 无向图:

如果不连通,那么经过多次Dfs形成深度优先生成森林(depth-first spanning forest);

6.2 双连通(biconnected):去掉一个顶点之后剩下的图仍然是连通的;

割点(articulation point)就是使图丧失连通性的顶点;
查找割点【很经典】:
在遍历的时候计算Num(w),low(w);

6.3 欧拉回路(任意顶点度为偶数):

检查算法:多次遍历:每一轮都找到一条回路,直到所有边都被遍历到为止;

6.4 有向图(背向边、前向边、交叉边):

查找强分支【很经典】:

练习总结:

9.13 二分图与二分匹配问题:
其中教师和课程的例子非常好,要牢记;
二分匹配的问题就是,两个顶点集,每个顶点匹配的边不能有两条或两条以上,如,每个教师只能上一门课,每门课只能由一个教师上;
问题c,探讨了网络流和二分匹配问题之间的关系;
可以在二分图两端加上s、t顶点,这样问题其实就等价于s到t的最大流了;

9.15 通过Prim和Kruskal两种方法求图9-82的最小生成树,可以发现最小生成树其实可能不唯一,当一个顶点它属于的边中最小权的边数大于1时;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 离散数学是计算机科学和数学的重要基础课程之一,它涵盖了逻辑、集合论、图论、关系代数等概念和方法。而期末考试对于检验我们对这门课程的理解和掌握程度至关重要。 离散数学期末考试复习资料的PDF文件是一份精华知识笔记,是为了帮助我们复习和回顾课程内容所准备的。这份资料包含了课程的关键概念、定理、证明以及例题等,是对于知识点的精炼总结。 这份资料对于我们复习而言非常有价值。首先,它提供了一种快速复习的方式,我们可以通过阅读笔记来回顾和巩固课程中的重要知识点。其次,它的内容通常由经验丰富的老师或学生整理而成,包含了精华部分,可以帮助我们集中精力学习最重要的内容,提高学习效率。此外,资料中的例题和习题也有助于我们检验自己的理解和应用能力。 当然,这份复习资料也有其局限性。首先,它仅仅是一份笔记,不可能包含所有的细节和内容。所以,在复习过程中还需要结合课本和其他学习资料一起使用。其次,复习应该是综合性的,除了阅读资料,还需要进行实际的练习和解题,以检验和巩固知识的掌握程度。 总的来说,离散数学期末考试复习资料的PDF文件是一份非常重要的复习资料,对于我们备考有很大的帮助。在复习过程中,我们应该充分利用这份资料,并结合其他学习资源进行全面复习,提高自己的理解和应用能力。同时,也要注意多做练习题,加深对知识点的理解和记忆。 ### 回答2: 离散数学期末考试复习资料一般包括精华知识笔记的PDF文件。这些笔记是针对离散数学的重要知识点和概念所整理的要点摘要。通过阅读这些笔记,学生可以系统地回顾并复习离散数学的各个方面。 这些精华知识笔记通常包括离散数学的基本概念、命题逻辑、谓词逻辑、集合论、图论、计数原理、代数结构等内容。每个部分的笔记会选择重要的定理、Lemma以及相关证明,并提供必要的例题和练习题,有助于学生加深理解。 通过阅读这些复习资料,学生可以巩固离散数学的知识,加深对概念和定理的理解,并通过例题和练习题提升解题能力。此外,这些资料还可以帮助学生整理复习思路,理清知识的脉络,提高备考效率。 为了更好地利用这些复习资料,学生可以结合课本和课堂笔记,对比理解,查漏补缺。同时,切记要自己动手做题,通过做题巩固记忆,培养解题的思维能力。 总之,离散数学期末考试复习资料的精华知识笔记PDF对于学生巩固知识、提高解题能力和备考效率起着重要的作用。通过认真复习这些资料,相信学生们能够在考试中取得好成绩。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值