数据结构与算法----图

定义:由顶点集和边集组成,G=(E,V),其中V(G)=顶点个数,E(G)=边数

注:线性表可以是空表,树叶可以是空树,但图不可以是空,即V一定是非空集

种类

无向图

若E为无向边,则图为无向图,边(v,w)和顶点相关联;

    • 顶点v的度是指依附于该顶点的边的条数,几位TD(v)。

    • 度为边数*2;握手定理

  • 路径

    • 顶点v1到顶点v2之间的一条路径是指顶点序列,v1,v3,v4······v2;

  • 回路

    • 第一个顶点到最后一个顶点相同的路径称为回路或环;

  • 简单路径

    • 在路径序列中,定点不重复出现的路径称为简单路径。

  • 简单贿赂

    • 除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路称为简单回路;

  • 路径长度

    • 路径上边的数目

  • 点到点的距离

    • 从顶点u出发到顶点v的最短路径若存在则此路径的长度称为从u到v的距离。若从u到v根本不存在路径,则记该距离为无穷。

  • 无向图中若从顶点v到顶点w有路径存在,则称v和w是连通的。

  • 若无向图中任意两个顶点都是连通的,则称图G为连通图,否则称为非联通图。

  • n个顶点的无向图若为连通图,则最少有n-1条边

  • n个顶点的无向图若为连通图,则最多有n-1里面取条边

     

有向图

若E为有向边,则图为有向图,边<v,w>v称为弧尾,w称为弧头,<v,w>不等于<w,v>;

    • 入度:是以顶点v为终点的有向边的数目,记为ID(v);

    • 出度:是以顶点v为起点的有向边的数目,记为OD(v);

    • 顶点v的度等于入度和出度之和,即TD(v)=ID(v)+OD(v).

    • 入度之和=出度之和=边数

  • 有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的

  • 若图中任何一对顶点都是强连通的,则称此图为强连通图。

  • 若为强连通图,则最少有n条边(形成回路)

简单图(数据结构常用)

简单图--1.不存在重复边;2.不存在顶点到自身的边;

多重图

多重图--图G中某两个节点之间的边数多于一条,又允许定点通过同一条边和自己关联,则G为多重图

子图

设有两个图G=(V,E)和G=(V,E),若V是v的子集,且E是E的子集,则称为G是G的子图

生成子图

若有满足V(G`)=V(G)的子图,则称其为G的生成子图

连通分量

连通图:n-1条边

非连通图最多Cn-1 2条边

无向图中的极大连通子图称为连通分量

有向图中的极大强连通子图称为有向图的强连通分量

生成树

连通图的生成树是包含途中全部顶点的一个极小连通子图

生成森林

在非连通图中,连通分量的生成树构成了非连通图的生成森林

边的权,带权图/网

边的权--在一个图中,每条边都可以标上具有某种含义的数值,该数值称为该边的权值。

带权图/网--边上带有权值的图称为带权图,也称网。

带权路径长度--当图是带权图时,一条路径上所有边的权值之和,称为该路径的带权路径长度。

特殊的图

无向完全图--无向图中任意两个顶点之前都存在边。若其顶点数为|V|=n;|E|=[0,n(n-1)/2];

有向完全图--有向图中任意两个顶点之前都存在方向相反的两条弧。若其顶点数为|V|=n;|E|=[0,n(n-1)];

稀疏图--边很少的图(|E|<|V|log|V|)

稠密图--便很多的图

树--不存在回路,且连通的无向图(n个节点的树一定有n-1条边)

图的存储

邻接矩阵

#define MaxVertexNum 100
typedef struct{
    char Vex[MaxVertexNum];
    int Edge[MaxVertexNum][MaxVertexNum];
    int vexnum,arcnum;
}MGraph;

节点数为n的图G=(V,E)的邻接矩阵A是n*n的。将G的顶点编号为v1,v2······,vn;则

A[ i ] [ j ]={1,若(vi,vj)或<vi,vj>是E(G)中的边

{0,若(vi,vj)或<vi,vj>不是E(G)中的边

无向图:第i个结点的度=第i行(或第i列)的非零元素个数

有向图:第i个结点的出度=第i行的非零元素的个数

第i个结点的入度=第i列的非零元素个数

第i个结点的度=第i行,第i列的非零元素个数之和

注:邻接矩阵求顶点的度/出度/入度的时间复杂度为O(|V|)

邻接矩阵法存储带权图(网)

#define MaxVertexNum 100                        //顶点数目的最大值
#define INFINITY 最大的int值                     //宏定义常量“无穷”
typedef char VertexType;                        //顶点的数据类型
typedef int EdgeType;                           //带权图中边上权值的数据类型
typedef struct{
    VertexType Vex[MaxVertexNum];               //顶点
    EdgeType Edge[MaxVertexNum][MaxVertexNum];  //边的权
    int vexnum,arcnum;
}MGraph;                                        //图的当前顶点数和弧度

空间复杂度:O(|V|^2)--只和顶点数相关,和实际的边数无关

适合用于存储稠密图

无向图的邻接矩阵是对称矩阵,可以压缩存储(只存储上三角区/下三角区)

性质

A^n的元素A^n[i] [j]等于由顶点i到顶点j的长度为n的路径数目

邻接表法(顺序+链式存储)

边结点的数量是2|E|

适合存储稀疏图

表示方法不唯一

typedef struct{
    AdjList vertices;
    int vexnum,arcnum;
}ALGraph;
typedef struct ArcNode{ 
    int adjvex;     //边弧指向哪个结点
    struct ArcNode *next;   //指向下一条弧的指针
    //InfoType info;        //边权值
}ArcNode;
typedef struct VNode{
    VertexType data;    //顶点信息
    ArcNode *first;     //第一条边/弧
}VNode,AdjList[MaxVertexNum];

十字链表--存储有向图

空间复杂度:O(|V|+|E|)

弧结点:弧顶点编号,弧头顶点编号,权值,弧头相同的下一条弧。

顶点结点(用数组顺序存取):数据域,该顶点作为弧头的第一条弧,该顶点作为弧尾的第一条弧。

指定顶点的出边:绿色线路;

入边:橙色线路

邻接多重表--存储无向图

边结点:边结点两个顶点编号:i,j,info(权值)iLink依附于顶点i的下一条边,jLink依附于顶点j的下一条边。

顶点结点:data数据域,firstedge:与该顶点相连的第一条边

空间复杂度:O(|V|+|E|)

基本操作

图的基本操作:

  • Adjacent ( G , x , y ):判断图 G 是否存在边< x , y >或( x , y )。

  • Neighbors ( G , x ):列出图 G 中与结点 x 邻接的边

  • InsertVertex ( G , x ):在图 G 中插入顶点 x 。

  • DeleteVertex ( G , x ):从图 G 中删除顶点 x 。

  • AddEdge ( G , x , y ):若无向边( x , y )或有向边< x , y >不存在,则向图 G 中添加该边。

  • RemoveEdge ( G , x , y ):若无向边( x , y )或有向边< x , y >存在,则从图 G 中删除该边。

  • FirstNeighbor ( G , x ):求图 G 中顶点 x 的第一个邻接点,若有则返回顶点号。若 x 没有邻接点或图中不存在 x ,则返回﹣1

  • NextNeighbor ( G , x , y ):假设图 G 中顶点 y 是顶点 x 的一个邻接点,返回除 y 之外顶点 x 的下一个邻接点的顶点号,若 y 是 x 的最后一个邻接点,则返回﹣1。

  • Get _ edge _ value ( G , x , y ):获取图 G 中边( x , y )或< x , y >对应的权值。

  • Set _ edge _ value ( G , x , y , v ):设置图 G 中边( x , y )或< x , y >对应的权值为 v 。

图的遍历

广度优先遍历(代码会写)

  • 找到与一个顶点相邻的所有顶点

  • 标记那些定点被访问过

  • 需要一个辅助队列:

    • FirstNeighbor ( G , x ):求图 G 中顶点 x 的第一个邻接点,若有则返回顶点号。若 x 没有邻接点或图中不存在 x ,则返回﹣1

    • NextNeighbor ( G , x , y ):假设图 G 中顶点 y 是顶点 x 的一个邻接点,返回除 y 之外顶点 x 的下一个邻接点的顶点号,若 y 是 x 的最后一个邻接点,则返回﹣1。

  • 代码实现

    bool visited [ MAX _ VERTEX _ NUM ]; //访问标记数组
    void BFSTraverse ( Graph G ){//对图 G 进行广度优先遍历
        for (i=0;i<G.vexnum;++i)
                visited[i]= FALSE;
        InitQueue (Q);
        for (i=0;i<G.vexnum;++i)
            if (!visited[i])
     BFS ( G , i );
    //访问标记数组初始化
    //初始化辅助队列 Q //从0号顶点开始遍历
    //对每个连通分量调用一次 BFS // vi 未访问过,从 vi 开始 BFS
    //广度优先遍历
     void BFS (Graph G,int v){  //从顶点 v 出发,广度优先遍历图 G 
        visit(v);               //访问初始顶点 v 
        visited[v]=TRUE;        //对 v 做已访问标记
        Enqueue(Q,v);           //顶点 v 入队列 Q 
        while(!isEmpty(Q)){
            DeQueue(Q,v);       //顶点 v 出队列
            for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
                //检测 v 所有邻接点
                if(!visited[w]){    // w 为 v 的尚未访问的邻接顶点//问顶点 w  
                visit(w);
                visited[w]=TRUE;//对 w 做已访问标记 
                EnQueue(Q,w);//顶点 w 入队列
                }// if  
        }// while
    }

深度优先遍历(代码会写)

bool visited [MAX_VERTEX_NUM];      //访问标记数组
void DFSTraverse(Graph G){      //对图 G 进行深度优先遍历 
    for(v=0;v<G.vexnum;++v)
        visited[v]=FALSE;       //初始化已访问标记数据 
    for(v=0;v<G.vexnum;++v)//本代码中是从 v =0开始遍历
        if(!visited[v])
            DFS(G,v);
}
 void DFS(Graph G,int v){       //从顶点 v 出发,深度优先遍历图 G
     visit(v);              //访问顶点 v 
     visited [v]=TRUE;      //设已访问标记
     for(w=FirstNeighbor(G,v);w>=0;w=NextNeighor(G,v,w))
         if(!visited[w]){       // w 为 u 的尚未访问的邻接顶点
            DFS(G,w);
         } // if 
}

时间复杂度:访问各节点所需的时间+探索各条边所需的时间

对无向图进行BFS/DFS遍历调用BFS/DFS函数的次数=连通分量数

对于连通图只需要调用一次。

最小生成树

连通图的生成树是包含全部顶点的一个极小连通子图。

对于一个带权连通无向图 G =( VE ),生成树不同,每棵树的权(即树中所有边上的权值之和)也可能不同。设 R 为 G 的所有生成树的集合,若 T 为 R 中边的权值之和最小的生成树,则 I 称为 G 的最小生成树( Minimum - Spanning - Tree , MST )

  • 最小生成树可能有多个,但边的权值之和总是唯一且最小的

  • 最小生成树的边数=顶点数﹣1。砍掉一条则不连通,增加一条边则会出现回路

  • 如果一个连通图本身就是一颗树,则其最小生成树就是它本身

  • 只有连通图才有生成树,非连通图只有生成森林。

prim算法(选点)

时间复杂度:O(|V|^2),适用于稠密图

从某一个顶点开始构建生成树;每次将代价最小的新顶点纳入生成树,直到所有顶点都纳入为止。

Kruskal算法(选边)

每次选择一条权值最小的边,使这两条边的两头联通(原本已经连通的就不选),直到所有结点都连通

时间复杂度:O(|E|log2|E|),适用于稀疏图。

最小路径问题

单源最短路径

  • BFS算法(无权图)

  • void BFS_MIN_Distance(Graph G,int u ){
        for (i=0;i<G.vexnum;++i){
            d[i]=∞;
            path[i]=-1;
        }
        d[u]=0;
        visited[u]=TRUE;
        EnQueue(Q,u);
        while(!isEmpty(Q)){
            DeQueue(Q,v);       //顶点 v 出队列
            for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w))
                if(!visited[w]){    // w 为 v 的尚未访问的邻接顶点//问顶点 w  
                d[w]=d[u]+1;
                path[w]=u;
                visited[w]=TRUE;//对 w 做已访问标记 
                EnQueue(Q,w);//顶点 w 入队列
                }// if  
        }// while
    }

  • Dijkstra算法(带权图,无权图)不适用于带负权值的图

各顶点间的最短路径

  • Floyd算法(带权图,无权图)O(V^3)

有向无环图

AOV网

拓扑排序

  • 从AOV网中选择一个没有前驱(入度为0)的顶点并输出。

  • 从网中删除该顶点和所有以它为起点的有向边。

  • 重复1和2直到当前的AOV网为空或当前网中不存在无前驱的顶点为止。

  • 时间复杂度O(|V|+|E|),采用邻接矩阵需O(|V|^2)

逆拓扑排序

对一个AOV网,如果采用下列步骤进行排序,称之为逆拓扑排序:

  • 从AOV网中选择一个没有后继(出度为0)的顶点并输出。

  • 从网中删除该顶点和所有以它为终点的有向边。

  • 重复1和2直到当前的AOV网为空。

AOE网

在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销,称之为用边表示的网络

只有一个入度为0的点(源点)

仅有一个出度为的顶点(汇点)

最大路径长度为关键路径

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值