图的基本概念,图的遍历、拯救007

图(graph):
表示“多对多”的关系
六度空间理论(Six Degrees Separation)
包含:
1.一组顶点:通常用V(Vertex)表示顶点集合
2.一组边:通常用E(edge)表示边的集合
边是顶点对:(v, w) 属于E, v , w 属于V
有向边<v, w>表示v 指向w的边(单行线)
不考虑重边和自回路

抽象数据类型定义:
1.类型名称:图
2.数据对象集:G(V, E)由一个非空的有限顶点集合v 和一个有限边集合Ezucheng
2.操作集:

常见术语:
有向图:边是有方向的,可能是单向或双向
无向图:边没有方向
带权重的图叫“网络”

怎么在程序中表示一个图:
1.邻接矩阵G[N][N] --- N个顶点从0到N-1编号
1 若<Vi, Vj>是G中的边
G[i][j] = 0 否则

对角元都是0 因为不允许自回路
所有点关于对角线对称(实际上我们把一条边存了两次)
问题:对于无向图的存储,怎样可以省一半空间?
用一个长度N(N+1)/2的一维数组A来存储这些元素Gij在A中的下标是:
(i*(i + 1) / 2 + j)
对于网络,只要把G[i][j]的值定义为边<Vi, Vj>的权重即可
若Vi和Vj之间没有边,则暂用0表示

用邻接矩阵表示一个图的好处:
1.直观、简单、好理解
2.方便检查任意一对顶点间是否存在边
3.方便找任一顶点的“邻接点”(有边直接相连的顶点)
4.方便计算任一顶点的度:(从该顶点发出的边的边数为“出度”, 指向该点的边的边数为“入度”
无向图:对应行(或列)非0元素的个数(也就是所有边的条数)
有向图:对应行非零元素的个数就是“出度”,对应列非零元素的个数就是“入度”

缺点:
1.浪费空间:存稀疏图的时候,点很多,但是边很少,有大量无效元素
对稠密图(特别是完全图)还是很合算的
2.浪费时间: 统计稀疏图中一共有多少条边

用邻接表示一个图:
G[n]为指针数组,对应矩阵每行一个链表,只存非零元素
一定要够稀疏才划算
好处:
1.方便找任一顶点的所有“邻接点”
2.节约稀疏图的空间(需要N个头指针, + 2E个结点(每个结点至少2个域)
3.方便计算任一顶点的度?
对于无向图:是的
对于有向图:只能计算“出度”, 需要构造“逆邻接表”(存指向自己的边,邻接矩阵的一列)
方便计算“入度”
不方便检查任意一对顶点间是否存在边

5.2 图的遍历
1.深度优先搜索(Depth Firsh Search, DFS)
伪代码:(走一步,看一步,走不通后返回到上一个能走通的结点)

类似于树的先序遍历:

void DFS(Vertex V)
{
    visited[V] = true;
    for(V  的每个邻接点 w)
        if(!visited[w])
            DFS(w);
}

若有N个顶点,E条边,时间复杂度是
用邻接表存储图:有O(N+E)
用邻接矩阵存储图:有O(N^2)


2.广度优先搜索(Breadth First Search, BFs)(感觉上应该和邻接表存储搭配更好,但是感觉邻接矩阵也应该合格搭配更好)
(个人感觉用这种遍历方式更好一些)
相当于树的层序遍历(计划性非常强,出队一个,访问一圈,访问的同时入队)

 1 void BFS(Vertex V)
 2 {
 3     visited[V] = true;
 4     EnQueue(V, Q);
 5     while(!IsEmpty(Q))
 6     {
 7         V = DeQueue(Q);
 8         for(V 的每个邻接点 w)
 9             if(!visited[w])
10             {
11                 vitsit[w] = true;
12                 EnQueue(w, Q);
13             }
14     }
15 }

若有N个顶点,E条边,时间复杂度是
用邻接表存储图:有O(N + E)
用邻接矩阵存储图: 有O(N^2)

每调用一次DFS(V)或BFS(V),就把V所在的强连通分量遍历了一遍。

 

图不连通怎么办(可能有一些孤立的点,他们和其他的结点都不连通)
连通:如果从v 到w存在一条(无向)路径,则称v和w是连通的
路径:v到w的路径是一系列顶点的集合,其中任意一对相邻的顶点间都有图中的边。
路径的长度:是路径中的边数(如果带权,则是所有边的权重和)。如果v和w之间的所有顶点都不同,则称简单路径
回路:起点等于终点的路径
连通图:图中任意两顶点都连通

对于不连通图:
连通分量:无向图的极大连通子图
极大顶点数:再加一个顶点就不连通了
极大边数:包含子图中所有顶点相连的所有边

强连通:有向图中顶点v和w之间存在双向路劲,则称v和w是强连通的
强连通图:有向图中任意两顶点均强连通
弱连通图:不符合强连通图的条件,但是若是将边的方向都去掉,如果是连通的,则称为弱连通图
强连通分量:有向图的极大强连通子图

图不连通时的遍历方法:

1 伪代码:
2 void ListComponents(Graph G)
3 {
4     for(each V in G)
5         if(!visited[V])
6             DFS(V);         //or BFS(V)
7 }

拯救007

void ListComponents(Graph G)
{
    for(each V in G)
        if(!visited[v])
            DFS(V);
}
 1 void Save007 (Graph G)
 2 {
 3     for(each V in G)
 4         if(!visited[V] && FirstJump(V)
 5         {
 6             answer = DFS(V);
 7             if(answer == Yes)
 8                 break;
 9         }
10     if(answer == Yes) output("Yes");
11     else output("No"):
12 }
 1 DFS 算法

2 int DFS(Vertex V) 3 { 4 visited[V] = true; 5 if(IsSave(v)) answer = Yes; 6 else { 7 for(each w in G) 8 if(!visited[w] && Jump(v, w) 9 { 10 answer = DFS[w]; 11 if(answer == yes) 12 break; 13 } 14 } 15 return answer; 16 }

 

posted @ 2018-08-25 20:36  Lucky小黄人^_^  阅读(910)  评论(0编辑  收藏  举报