第六章 图——存储结构和遍历

图的基本概念

图的概念

图Graph:  顶点Vertex;  弧 Arc。(至少有一个顶点)

有向图Digraph:有向弧; A->B(弧头B/弧尾A); 有向完全图。 G=<v,a>

无向图Undigraph:无向边  Edge; 无向完全图.                      G=(v,a)

邻接点:无向图如A与B互为邻接点,有向图如A邻接到B  

无向图顶点的度, TD(A)=2;

有向图顶点度, ID(A)=1,OD(A)=2,TD(A)=3

路径

图的分类

1.DG(有向图)、UDG(无向图)、

2.DN(网络,含权的有向图)和UDN(弧或边含权的无向图)。

3.稀疏图和稠密图

4.连通图 和非连通图

连通图  (无向)

在无向图G=(V, E)中,对任意两顶点u、v,都存在u到v的路径。

强连通图  (有向)

在有向图G=(V, E)中,对任意两顶点u、v,都存在u到v的有向路径

极大连通子图  (连通分量)

在非连通图中

极小连通子图  (生成树)

在连通图中

条件:

1.图中全部顶点n

2.足以构成一颗树的n-1条边

特点:

没有回路

如图,有5个点,找4条边 

图的存储结构

邻接矩阵表示法

方法:顶点表  +  邻接矩阵(存储顶点间关系)

特点:

无向

 

有向

图的数组(邻接矩阵)存储表示 

typedef *** VertexType;//定义顶点类型
#define MVNUM  100  //最大顶点个数
#define  INFINITY   32767 //最大值∞ 
typedef enum{DG, DN, UDG, UDN}  GraphKind;//图类型:有向无向等等 
 // 弧的定义
typedef struct ArcType {  
    int/double  adj;    //邻接数,0/1或w/∞
    InfoType  *info;  //弧的附加信息,InfoType可为char和int
}ArcType;
// 图的定义
typedef struct { 
     VertexType  vexs[MVNUM]; //顶点信息 
     ArcType    arcs[MVNUM][MVNUM];   // 邻接矩阵
     int    vexnum, arcnum;   // 顶点数,弧数      
     GraphKind   kind;     // 图的类型标记             
} MGraph;   //矩阵图

 创建

 //输入图的种类及顶点和边信息构造图G(邻接矩阵表示)
Status CreateGraph(Mgraph &G) {
     scanf(“%d”,&G.kind);//枚举值DG,DN…实输入0 1 …
     switch (G.kind) {
          case DG : return CreateDG(G);
          case DN : return CreateDN(G);
          case UDG : return CreateUDG(G);
          case UDN : return CreateUDN(G);
          default : return ERROR;
}
 //建立无向网G
Status CreateUDN(Mgraph &G){
  cin>>G.vexnum>>G.arcnum>>IncInfo; //IncInfo为0表弧无附加信息
    for (i=0 ; i< G.vexnum; ++i) cin>>G.vexs[i]; 
    for (i=0 ; i< G.vexnum; ++i)
        for(j=0 ; j< G->vexnum; ++j)
             G.arcs[i][j] = {INFINITY, NULL};  //各弧初始化
    for (k=0;k< G.arcnum;++k){
        scanf(“%c %c %lf”,&v1,&v2,&w);  //输入一“边”及其权值
        i=LocateVex(G,v1);j= LocateVex(G,v2) ; //确定顶点下标
       G.arcs[i][j].adj = w;
       if (IncInfo)  cin>>G.arcs[i][j].info;
       G.arcs[j][i]= G.arcs[i][j]; //无向网需将对称弧加入
    }
} //END

优缺点 

优点:易于求顶点度(区分有/无向图)、求邻接点,易判断两点间是否有弧或边相连

缺点:不利于稀疏图的存储,因弧不存在时也要存储相应信息;且要预先分配足够大空间

邻接表表示法

方法:顶点数组,每个数组元素包含两部分:data + firstarc(邻接点下标所构成的一个链表)

无向

有向 

 

邻接表存储结构定义

typedef struct ArcNode {  
    int        adjvex;
    struct ArcNode * nextarc; 
    InfoType  *info;
} ArcNode;
typedef struct VNode { 
     VertexType  data; 
     ArcNode  *firstarc; 
}VNode, AdjList[MVNUM];
typedef struct {  
     AdjList  vertices;
     int    vexnum, arcnum; 
     GraphKind   kind;
  } ALGraph;  //邻接表图

创建

Status CreateDN(ALGraph &G){ //建有向网
    scanf(“%d%d%d”, &G.vexnum, &G.arcnum, &IncInfo);
    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>>w;
        i=LocateVex(G,v1);  j= LocateVex(G,v2);
        arc=new ArcNode;
        arc->adjvex=j; 
        if (IncInf) cin>>arc->info;
        arc->nextarc = G.vertices[i].firstarc; //插入到开始位置
        G.vertices[i].firstarc=arcn;//邻接点与输入逆序排列,正序?
    }                           //邻接点顺序依赖输入顺序和创建程序,默认正序
    Return OK;
 } //思考无向图的创建还有何不同?

 十字链表表示法(略)

 总结

图的遍历

图遍历的概念

深度优先遍历(Depth-First Searching)——dfs

原理

和树的先根遍历一样

算法实现

邻接矩阵

 邻接表

两种方法的实现代码 

int visited[G.vexNum];  // 定义一个全局数组visited,用于记录每个顶点是否被访问过

// DFS函数的声明和实现
void DFS(Graph G, int v, Status (*visit)(ElemType));

void DFS(Graph G, int v, Status (*visit)(ElemType)){
    visit(v);  // 对当前顶点进行访问操作
    visited[v] = TRUE;  // 将当前顶点标记为已访问
    for(w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w)) {
        if (!visited[w]) {  // 如果相邻顶点未被访问过
            DFS(G, w, visit);  // 递归调用DFS函数,继续访问相邻顶点
        }
    }
    return;  // 返回上一层递归
}

// DFSTraverse函数的声明和实现
void DFSTraverse(Graph G, Status (*visit)(int v));

void DFSTraverse(Graph G, Status (*visit)(int v)){
    for (int v = 0; v < G.vexnum; ++v) {  // 初始化visited数组,将所有顶点标记为未访问
        visited[v] = FALSE;
    }
    for (int v = 0; v < G.vexnum; ++v) {  // 遍历所有顶点
        if (!visited[v]) {  // 如果顶点未被访问过
            DFS(G, v, visit);  // 调用DFS函数,从该顶点开始进行深度优先搜索
        }
    }
}

复杂度:每个顶点调用一次DFS,DFS主要操作是查找邻接点

邻接矩阵存储时查找一顶点邻接点的复杂度为O(n),总复杂度O(n2);

邻接表存储时查找所有顶点所有邻接点复杂度为O(e),总复杂度为O(n+e)

广度优先遍历( Breadth-First Searching )——BFS

原理

算法实现

访问出队的邻接点

void  BFS(Graph G, int v, Status (*visit)(ElemType){
    visit (v);  visited[v]=TRUE; 
    InitQueue(Q);  EnQueue(Q, v);             
    while (!QueueEmpty(Q))  {
       DeQueue(Q, v); 
      for(w=FirstAdjVex(G,v); w>=0;w=NextAdjVex(G,v,w))
            if ( ! visited[w] ) { 
                 visit(w);  visited[w]=TRUE;  EnQueue(Q, w); 
            }
       } 
}
void BFSTraverse(Graph G,  Status (*visit)(int v)) {
      for (v=0; v<G.vexnum; ++v)   visited[v] = FALSE; 
      for (v=0; v<G.vexnum; ++v) 
            if (!visited[v]) BFS(G, v, visit);
} //教材中将此处两个函数代码合并为了一个函数

复杂度:每个顶点进一次队,出队时主要操作是找邻接点,

邻接矩阵存储时查找某顶点的邻接点复杂度为O(n),总复杂度O(n2);

用邻接表存储时查找所有定点所有邻接点复杂度O(e),总复杂度为O(n+e)

图遍历的应用

1.走出迷宫

2.循环依赖与死锁问题    等同于判断是否存在有向回路

3.应用:迷宫最短路 连通性与连通分量 生成树

结论:dfs过程中,从v出发找到邻接点w,若w是已访问过的祖先结点,则<v,w>必是一条回路的边

总结与推广

总结

回溯实现

推广

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值