1.什么是图
也是一种数据结构,由顶点的集合和边的集合组成。其中,边是指顶点的偶对。
表示方法:G=(V,E)
边数较少的称为稀疏图(V<|V|log|V|),相反,边数较多的则称为密集图,所有可能边都存在的叫做完全图。
边有方向性的图称为有向图,否则称为无向图
若图中的各个顶点均带有标号,则为标号图。
边上带有权值的称为带权图。
如果图上的一条路径各个顶点互不相同,则称为简单路径。
如果一条路径将某个顶点连接到它本身且路径长度不小于3,则称为回路。
如果构成回路的路径是简单路径,称其为简单回路。
子图,顶点集的子集及其关联边构成的图。
连通:无向图中的任意一个顶点到其他任意顶点都至少有一条路径。
连通分量:无向图的最大连通子图,称为该无向图的一个连通分量。
无环图:不带回路的图。
有向无环图:(DAG)不带回路的有向图。
2.图的两种表示法
1)相邻矩阵,空间代价为O(|V|^2)
2)邻接表,以链表为元素的数组,第i个位置的元素存储的是指向(顶点vi的边表)的指针。顶点vi的边表是vi所指顶点构成的链表。空间代价为O(|V|+|E|),由此可见,邻接表的实现适用于稀疏图。
3.图的周游方式
1)深度优先搜搜DFS
沿着图的某一支搜索直到其末端。然后回溯,沿着另一支搜索。每搜索至一个顶点,将其标记入栈,再访问其某个未被访问的相邻结点(即仅仅对栈顶元素调用DFS)。
回溯的时候,如果一个顶点不再有任何未访问相邻结点,则将其出栈。
可见DFS对每个顶点访问一次,如果是有向图,则恰好也对每条边处理一次(或许处理的结果是忽略此边,因为边的顶点可能已经被访问过了);而如果是无向图,DFS对每条边分别沿两个方向处理,所以总的时间代价是O(|V|+|E|)
2)广度优先搜索BFS
与DFS的区别是,在访问其他顶点之前,先检查起点的所有邻接点。
对于一个起点,将其所有的邻接点放进队列中,再从队首取出继续BFS。
3.拓扑排序
可以理解为一个有向无环图,任务与任务之间存在着一定的优先级关系。
拓扑排序可以利用深度优先算法实现,在某顶点以及要被弹出栈的时候,打印该顶点,如此即可得到一个拓扑排序的逆序。
另一种实现:将所有无前驱(说明该任务无先决条件)放入队列,再依次取出并打印,取出时以此顶点为前驱的所有顶点的数目全部减一。当某顶点的前驱数目减到0时就入列。这样得到的是顺序的拓扑序列。
4.最短路径问题
单源最短路径问题:
dijkstra算法
经典算法。
floyd算法:
k-path:所谓k-path,即从u到v的路径中除了u和v两个顶点外,任意一个中间顶点的序号的大小必须小于k。特殊的,0-path是指u,v直接相连的路径。
floyd利用最短k-path来检验:
如果u,v之间的现在存在一个最短k-path,则k+1-path或者经过K,或者不经过K。如果经过K,则说明u,v之间的最短k-path就等于u,k之间的最短k-path与k, v之间的最短k-path;如果不经过k则保持不变;
floyd要求出全局最短路径,则要对每个顶点进行检查,对于某个顶点,又要检查其到其他所有顶点的最短路径,两两之间还要检查序号k的所有顶点,因此需要三重循环。时间复杂度是O|V^3|.
5.最小支撑树(MST)
一类子图:包括图的所有顶点;其边上权的和是所有子图中最小的;连通的;
最小支撑树的算法:
1)prim算法:首先任选一个顶点加入到MST集合中,在集合外选择一个对于距离集合中顶点最近的一个顶点,将该顶点和该顶点到MST集合的关联边加入到集合中,直到集合外不剩任何顶点。
实际上是一种贪心算法。
2)Kruskal算法
也是一种贪心算法
首先将图化为|V|个等价类。每次选择关联边权值最小的两个等价类进行合并,最后只剩一个等价类,即为MST。