基本概念

二元组(V, E) 称为图。V为顶点的集合,E为V中顶点之间的边的集合。

自环:一条边的两个端点是重合的。

重边:两个端点之间有两条以上的边

简单图:没有自环和重边的图

左图是简单图,右图中存在环和重边。

边和向

无向边:边是双向的

有向边:单向边,有箭头

无向图:只有无向边的图

有向图:只有有向边的图

顶点的度

无向图中,一个顶点相连的边数称为该顶点的度。

有向图中,从一个顶点出发的边数称为该顶点的出度;到达该顶点的边数称为它的入度。

权和网

在图的边给出相关的数,称为权。

权可以表示一个顶点到另一个顶点的距离,耗费等。带权图一般称为网。

图的种类

完全图:任何两个顶点之间都有边相连的图

n阶完全图的任意一点v有:d(v)=n-1

非完全图:至少有两个顶点之间无边相连的图

稀疏图:边很少的图

稠密图:边很多的图

利用图解决数学竞赛题目好牛逼啊

例:有一个1982人组成的团体,其中任意4人中至少有一人认识其他三人,求至少有多少人认识这个团体中其他所有人。(额,有点难啊

分析:转换为图,人为点,认识为边。显然,最多是全部人互相认识,即该图为完全图,但该题求最小数,即求非完全图中d(v)=1981的点的最小个数。

设u,v之间无边,则d(u)<=1980,d(v)<=1980,再设另外两点x,y之间也无边,则不符合题意。所以x,y必定有边,且其中至少有一个点与u,v有边,则至少有1979个点与其他点都有边。

 图的邻接矩阵表示法

相邻矩阵是表示结点间相邻关系的矩阵。

若G=(V,E)是一个具有n个结点的图,则G的相邻矩阵是如下定义的二维数组a,其规模为n*n

 A(i,j)= 1 表示顶点i和顶点j有边

 A(i,j)= 0 表示顶点i和顶点j无边

有就是,两点为1就有连线,为0无连线

邻接矩阵为

 

同样,网和有向图表示为:

                                        

 空间复杂度:O(V^2)

优点:直观,容易理解,可以直接查看任意两点的关系。

缺点:对于稀疏图,会有很多空间根本没有利用。不能处理重边。要查询某一个顶点的所有边,要枚举V次。

图的邻接链表表示法(主要应对稀疏图)

对图的每个顶点建立一个单链表(n个顶点建立n个单链表),第i个单链表中的结点包含顶点Vi的所有邻接顶点。

空间复杂度:有向图O(V+E)无向图O(V+2*E)   

优点节省空间,能快速找到某个顶点所有相连的顶点,而无需访问无关顶点。

对于大多数图来说都是稀疏图,所以务必掌握用邻接链表来存储图。

邻接链表

struct Edge//存储每条边
{
int v,w,next
}e[maxn];
int k=1,head[maxn];//每条链表的第一条边
void adde(int u,int v,int w)//加边
{
    e[k].v=v;
    e[k].w=w;
    e[k].next=head[u];
    head[u]=k++;
}

OK!

拓扑排序

啥是拓扑排序

拓扑排序是对有向无环图(Directed Acyclic Graph简称DAG)求出一个顶点序列,使其满足:对于任意边(u,v)ϵE,u在序列的位置总在v之前。 

以上摘自百科

怎么做

  1. 从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它.   
  2. 从图中删去该顶点,并且删去从该顶点发出的全部有向边,并且更新点的入度.  
  3. 重复上述两步,直到剩余的图中不再存在没有前趋的顶点为止.

搞爪子?

  1.  拓扑排序在有向无环图中才能排出有效的序列,否则能判断该有向图有环
  2. 如果输入的有向图中的点,不存在入度为0的点,则该有向图存在回路
  3. 如果存在的入度为0的点大于一个,则该有向图肯定不存在一个可以确定的拓扑序列但并不妨碍拓扑排序

邻接矩阵+直接法

空间开销大,不能判断有环

for(i=1;i<=n;i++)//外层循环n次,in[]数组用来记录每个点的入度    
{   
        j=1;   
        while(in[j]!=0) j++;//从第一个节点开始找到一个节点入度为0的节点  
        ans[i]=j;//存储答案    
        in[j]=-1;//将该节点的入度更新为-1    
        for( k=1;k<=n;k++)//将所有与节点j相连的节点的入度值全部减1    
            if(vis[j][k]==1in[k]--;   
} 

 

邻接链表+栈

空间小,可以判断环

bool topsort()//拓扑排序,有拓扑序返回真,否则返回假 
{
    int p,q;
    for(int i=1;i<=n;i++)//先找到入度为0的节点入栈 
    {
        if(!d[i])s.push(i);//d[i]表示i点的入度,在加边的时候初始化d[i]
    }
    while(!s.empty())//当栈非空进行操作 
    {
        p=s.top();//记录栈顶节点 
        s.pop();//弹出栈顶节点 
        ans[cnt++]=p;//将弹出节点存储到结果数组,并计数 
        for(int i=head[p];i!=-1;i=e[i].next)//清除该节点的出度 
        {
            q=e[i].v;
            if(!(--d[q]))s.push(q);//如果又发现入度为0的节点,继续入栈 
        }
    }
    if(cnt<=n)return false;//当有节点没入栈,则说明存在环 
    return true;
}

QAQ¤^¤……

版权声明:本文为mzyczly原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/mzyczly/p/11024914.html