数据结构之图


数据结构之图

图的定义和术语

图:G=(V,E)
V:顶点的有穷非空集合;
E:边的有穷集合;

无向图:每条边都是无方向的
有向图:每条边都是有方向的

完全图:任意两个点都有一条边相连

稀疏图:有很少边或者弧的图
稠密图:有较多边或弧的图
:边、弧带权的图
邻接:有边、弧相连的两个顶点之间的关系
存在(vi,vj),则称vi和vj互为邻接点
存在<vi,vj>,则称vi邻接到vj,vj邻接于vi
**关联(依附)**:边、弧与顶点之间的关系,存在(vi,vj)/<vi,vj>,则称该边/弧关联于vi和vj

顶点的度:与该顶点相关联的边的数目,记为TD(v)

在有向图中,顶点的度等于该顶点的入度与出度之和,顶点v的入度是以v为终点的有向边的条数,记为ID(v);顶点的出度是以v为始点的有向边的条数,记作OD(v)

路径:接续的边构成的顶点序列
路径长度:路径上边或弧的数目/权值之和
回路:第一个顶点和最后一个顶点相同的路径
简单路径:除路径起点和终点可以相同外,其余顶点均不相同
简单回路:除路径起点和终点相同外,其余顶点均不相同

连通图(强联通图)
任意两个顶点都存在路径

权与网:图中边或弧所具有的相关数称为权,表明从一个顶点到顶一个顶点的距离或耗费
子图

**连通分量(强连通分量)**:
无向图

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

极小连通子图:该子图是G的连通子图,在该子图中删除任意一条边会使子图不再连通
生成树:包含无向图G所有顶点的极小连通子图
生成森林:对非连通图,由各个连通分量的生成树的集合

图的存储结构

1.数组表示法(邻接矩阵)
2.链式存储结构:邻接表、邻接多重表、十字链表

数组表示法(邻接矩阵)

建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点之间的关系)

无向图的邻接矩阵表示法

有向图的邻接矩阵表示法


网(有权图)的邻接矩阵表示法

邻接矩阵的存储表示

用两个数组分别存储顶点表和邻接矩阵

#define MaxInt 32767 //表示极大值
#define MVNum 100 //最大顶点数
typedef char VerTexType;//设定点的数据类型为字符型 
typedef int ArcType;//假设边的权值类型为整型  

typedef struct {
    VerTexType vexs[MVNum];//顶点表
    ArcType arcs[MVNum][MVNum]; //邻接矩阵
    int vexnum,arcnum;//图的当前点数和边数
}AMGraph;

算法1 采用邻接矩阵表示法创建无向图
Status CreateUND(AMGragh &G){
   cin>>G.vexnum>>G.arcnum;//输入总顶点数、总边数  
   for(i=0;i<G.vexnum;++i){
    cin>>G.vex[i];//依次输入点的信息
   }
   for(i=0;i<G.vexnum;++i)
    for(j=0;j<G.vexnum;++j)
     G.arcs[i][j]=Maxint;//边的权值均置为极大值
   
   for(k=0;k<G.arcnum;++k){
    //构造邻接矩阵
    cin>>v1>>v2>>w;//输入一条边所依附的顶点及边的权值  
    i=LocateVex(G,v1);
    j=LocateVex(G,v2);//确定v1和v2在G的位置
     G.arcs[i][j]=w;
     G.arcs[j][i]=G.arcs[i][j];//边<v1,v2>的权值设置为w,置对称边权值为w
   }
   return OK;
}
补充算法 在图中查找顶点
int LocateVex(AMGraph G,VertexType u){
    //在图G查找顶点u,返回顶点下标
    int i;
    for(i=0;i<G.vexnum;++i){
        if(u==G.vex[i])return i;
    }
    return -1;
}

邻接矩阵的优缺

优点:

缺点:

邻接表表示法(链式)

无向图:

有向图:

邻接表的存储表示

顶点的结点结构

typedef struct VNode{
    VerTexType data;//顶点信息
    ArcNode *firstarcs; //指向第一条依附该顶点的边的指针
}VNode,AdjList[MVNum];

例如AdjList v;相当于:VNode v[MVNum];

边结点结构

#define MVNum 100//最大顶点数
typedef struct ArcNode{
    //边结点
    int adjvex//该边所指向的顶点位置
    struct ArcNode *nextarc;//指向下条边的指针
    OtherInfo info; //和边相关的信息
}ArcNode;

图的结构定义

typedef struct {
    AdjList vertices;//vertices--vertex的复数
    int vexnum,arcnum;//图的当前顶点数和弧数
}ALGraph;

邻接表操作举例说明:

采用邻接表表示法创建无向图

Status CreateUDG(ALGraph &G) {
    //采用邻接表表示法,创建无向图G
    cin>>G.vexnum>>G.arcnum;
    for(i=0;i<G.vexnum;++i){
        cin>>G.vertices[i].data;
        G.vertices[i].firstarc=NULL;
    }
    for(k=0;k<G.arcnum;++k){
        cin>>v1>>v2;
        i=LocateVex(G,v1);
        j=LocateVex(G,v2);
        p1=new ArcNode;
        p1->adjvex=j;
        p1->nexrarc=G.vertices[i].firstarc;
        G.vertices[i].firstarc=p1;
        p2=new ArcNode;
        p2->adjvex=i;
        p2->nexrarc=G.vertices[j].firstarc;
        G.vertices[j].firstarc=p2;
    }
    return OK;
}

邻接表的优缺


邻接矩阵与邻接表的关系


可供选择的其他存储结构

十字链表

顶点结点:

firstin:第一条入弧
firstout:第一条出弧

弧结点:

tailvex:弧尾位置
headvex:弧头位置
hlink:弧头相同的下一条弧
tlink:弧尾相同的下一条弧

邻接多重表

图的遍历-深度优先

定义:从已给的连通图中某一顶点出发,沿着一些边访遍图中的所有顶点,且使每个顶点仅被访问一次

遍历实质:找到邻接点

图的特点:
图中可能存在回路,且图的任一顶点都可能与其他顶点相通,在访问完某个顶点之后可能会沿着某些边又回到曾经访问过的顶点,如何避免?


邻接矩阵表示的无向图深度遍历实现

void DFS(AMGraph G,int v){
    //图G为邻接矩阵类型
    cout<<v;
    visited[v]=true;//访问第v个顶点
    for(w=0;w<G.vexnum;w++)//依次检查邻接矩阵v所在的行
    if((G.arcs[v][w]!=0)&&(!visited[w]))DFS(G,w);
    //w是v的邻接点,如果w未访问,则递归调用DFS
}

深度遍历效率

非连通图的深度优先遍历

图的遍历-广度优先

邻接表表示的无向图广度遍历实现

利用队列实现

F:队头指针 R:队尾指针

void BFS(Graph G,int v){
    //按广度优先非递归遍历图G
    cout<<v;
    visited[v]=true;//访问第v个顶点
    InitQueue(Q);//辅助队列Q初始化,置空  
    EnQueue(Q,v);//v进队  
    while(!QueueEmpty(Q)){//队列非空
        DeQueue(Q,u);//队头元素出队并置为u
        for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))
        if(!visited[w]){
            //w为u的尚未访问的邻接顶点
            cout<<w;visited[w]=true;
            EnQueue(Q,w);//w进队
        }
    }
}

广度遍历与深度遍历的比较

最小生成树

回顾:生成树:所有顶点均由边连接在一起,但是不存在回路的图

·一个图可以有许多棵不同的生成树
·所有生成树具有以下共同特点:
1.生成树的顶点个数与图的顶点个数相同
2.生成树是图的极小连通子图
3.在生成树中再加一条边必然形成回路
4.生成树中任意两个顶点间的路径是唯一的

无向图的生成树

利用优先遍历即可

深度优先遍历形成的生成树

广度优先遍历形成的生成树

最小生成树的定义

给定一个无向网络,在该网的所有生成树中,使得各边权值之和最小的那颗生成树称为最小生成树

构造最小生成树

MST性质(一种贪心算法):设N=(V,E)是一个连通网,U是顶点集V的一个非空子集。若边(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必存在一棵包含边(u,v)的最小生成树。

构造最小生成树方法1-Prime算法

算法思想: 不断扩大最小生成树的范围

构造最小生成树方法2-Kruskal算法

算法思想:直接选当前最小的边

注:最小生成树可能不唯一

两种算法的比较

最短路径

顶点:表示地点
弧:表示两地连通
弧上的权值:表示距离或花费等等

第一类问题:两点间的最短路径

所有顶点间的最短路径-弗洛伊德算法

第二类问题:某源点到其他各点最短路径

单源最短路径-迪杰斯特拉算法

单源最短路径-迪杰斯特拉算法



所有顶点间的最短路径-弗洛伊德算法

拓扑排序

有向无环图:

拓扑排序例子


AOV网的特点:

拓扑排序定义

拓扑排序方法

简言之:一直选择没有前驱的点

检测AOV网中是否存在环的方法

关键路径

例:

把工程计划表示为边表示活动的网络,即AOE网,用顶点表示事件,弧表示活动,弧的权表示活动持续的时间

关键路径:路径长度最长的路径
路径长度:路径上各活动持续时间之和

求关键路径步骤:

时间余量为0即为关键活动


文章作者: autumnwt
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 autumnwt !
  目录