数据结构第七章——图

一、图的定义和术语

(1)无向图、有向图

(2)顶点、边、弧、弧头、弧尾:数据元素vi称为顶点(vertex );(vi, vj)表示在顶点vi和顶点vj之间有一条直接连线。 在无向图中,则称这条连线为边;边用顶点的无序偶对(vi, vj)来表示,称顶点vi和顶点vj互为邻接点,边(vi, vj)依附于顶点vi与顶点vj; 在有向图中,一般称这条连线为弧。弧用顶点的有序偶对<vi, vj>来表示,有序偶对的第一个结点vi被称为始点(或弧尾),在图中就是不带箭头的一端;有序偶对的第二个结点vj被称为终点(或弧头),在图中就是带箭头的一端。

(3)无向完全图:无向图中任意两点都有一条边相连接,n个顶点,n(n-1)/2条边

(2)有向完全图:任意两顶点之间都有方向互为相反的两条弧相连,n个顶点,n(n-1)条边

(4)稠密图,稀疏图:边数多少

(5)顶点的度、入度、出度

(6)边的权、网:与边有关的数据信息为权

(7)路径、路径长度:路径上边的数目为长度

(8)回路、简单路径、简单回路:序列中顶点不重复出现的路径为简单路径。

(9)子图

(10)连通的、连通图、连通分量:在无向图中

 (11)强连通图、强连通分量:有向图中

 (12)生成树:假设一个连通图有 n 个顶点和 e 条边, 其中 n-1 条边和 n 个顶点构成一个极小连通子图, 称该极小连通子图为此连通图的生成树。

(13)生成森林:对非连通图,称各个连通分量生成树的集合为此非连通图的生成森林。


二、图的存储结构

1.领接矩阵(数组)

 

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

#define  INFINITY   INT_MAX                 //最大值   ∞
#define  MAX_VERTEX_NUM    20          //最大顶点个数
typedef  enum { DG,DN,UDG,UDN}  GraphKind;
typedef  struct  ArcCell{
	VRType      adj; 		//弧是否相通,或权值
	InfoType    *info;   		//弧的相关信息
}ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

typedef  struct {
	VertexType    vexs[MAX_VERTEX_NUM]; //顶点向量
	AdjMatrix     arcs;				   //邻接矩阵
	int     vexnum, arcnum;	//图的当前顶点和弧数
	GraphKind     kind;		//图的种类标志
}Mgraph;


在邻接矩阵的存储结构上创建图
Status CreateGraph( MGraph &G ) 
{   scanf(&G.kind); // 自定义输入函数,读入一个随机值
    switch (G.kind) 
   {
    case  DG: return CreateDG(G);   // 构造有向图G
    case  DN: return CreateDN(G);   // 构造有向网G
    case UDG: return CreateUDG(G);  // 构造无向图G
    case UDN: return CreateUDN(G);  // 构造无向网G
    default : return ERROR;
    }
} // CreateGraph

2.邻接表(链表)

邻接表存储表示:
#define  MAX_VERTEX_NUM     20 
    
typedef  struct  ArcNode
{    	int  adjvex;
           struct ArcNode    *nextarc;
           InfoType   *info;
}ArcNode;              //边表结点


typedef  struct   VNode
 {  VertexType  data;
 	ArcNode *firstarc;
  }Vnode,  AdjList[MAX_VERTEX_NUM];  //表头结点

typedef   struct 
{     AdjList  vertices;
       int   vexnum , arcnum;      //顶点数和弧数       
       int   kind;                           //图的种类标志
}ALGraph;

建立无向图的邻接表:
CreatAdjList(ALGraph ga ) //顶点数目n,边的数目e
 { int i,j,k; ArcNode *s;
     for (i=0;  i<n; i++)
      {  ga. vertices [i]. data =getchar( );  
         ga. vertices [i]. firstarc =NULL; }
     for (k=0; k<e; k++)
      {  scanf(“%d%d”,&i,&j);
          s=(ArcNode *)malloc(sizeof(ArcNode)); //采用头插入法
          s->adjvex=j;   s-> nextarc = ga. vertices [i]. firstarc; 
	    ga. vertices [i]. firstarc =s;

 	    s=(ArcNode *)malloc(sizeof(ArcNode));
	    s->adjvex=i;   s-> nextarc = ga. vertices [j]. firstarc; 
	    ga. vertices [j]. firstarc =s;
       }
    }

 

3.十字链表

4.邻接多重表

三、图的遍历

1.深度优先搜索DFS:

(1)基本思想:

 

 (2)非连通图的DFS

 (3)算法实现

void DFS(ALGraph *G , int v)

{   LinkNode *p ;
    Visited[v]=TRUE ;
    Visit[v] ; /* 置访问标志,访问顶点v */
    p=G->AdjList[v].firstarc; /* 链表的第一个结点 */
    while (p!=NULL)
      { if (!Visited[p->adjvex]) DFS(G, p->adjvex) ;
      /* 从v的未访问过的邻接顶点出发深度优先搜索 */
      p=p->nextarc ;
      }
}


void DFS_traverse (ALGraph *G)

{   int v ;
    for (v=0 ; v<G->vexnum ; v++)
    Visited[v]=FALSE ; /* 访问标志初始化 */

    
    for (v=0 ; v<G->vexnum ; v++)
    if (!Visited[v]) DFS(G , v);
}

2.广度优先搜索BFS:

(1)从图中某个顶点v0出发,首先访问v0  ;

(2)依次访问v0各个未被访问的邻接点;

(3)分别从这些邻接点出发,依次访问它们的各个未被访问的邻接点。

(4)算法实现

typedef enum {FALSE , TRUE} BOOLEAN ;
BOOLEAN Visited[MAX_VEX] ;
typedef struct Queue
{ int elem[MAX_VEX] ;
int front , rear ;
}Queue ; /* 定义一个队列保存将要访问顶点 */


void BFS_traverse (ALGraph *G)
{ int k ,v , w ;  LinkNode *p ; Queue *Q ; Q=(Queue *)malloc(sizeof(Queue)) ;
Q->front=Q->rear=0 ;  /* 建立空队列并初始化 */
for (k=0 ; k<G->vexnum ; k++) Visited[k]=FALSE ;  /* 初始化 */
for (k=0 ; k<G->vexnum ; k++) 
    { v=G->AdjList[k].data ; /* 单链表的头顶点 */
    if (!Visited[v]) /* v尚未访问 */
        { Q->elem[++Q->rear]=v ; /* v入队 */
             while (Q->front!=Q->rear)
                 { w=Q->elem[++Q->front] ;
                    Visited[w]=TRUE ; /* 访问标志 */
                    Visit(w) ; /* 访问队首元素 */
                     p=G->AdjList[w].firstarc ;
                          while (p!=NULL)
                                { if (!Visited[p->adjvex])
                                    Q->elem[++Q->rear]=p->adjvex ;
                                     p=p->nextarc ;  }   }   }   }    }

四、图的连通性问题

1.无向图的连通分量和生成树

树的孩子兄弟表示法:
typedef   struct  CSNode
{
	ElemType  data;
	struct  CSNode   *firstchild,*nextsibling;
}CSNode, *CSTree;


建立无向图G的DFS生成森林的孩子兄弟链表T
void DFSForest(Graph G, CSTree &T) 
{
  T = NULL;
  for (v=0; v<G.vexnum; ++v)  visited[v] = FALSE;
  for (v=0; v<G.vexnum; ++v)
	if (!visited[v]) 
	{   // 第v顶点为新的生成树的根结点
		p= (CSTree)malloc(sizeof(CSNode));//分配根结点
		p->data=GetVex(G,v);     // 给该结点赋值
		p->firstchild=NULL;
		p->nextsibling=NULL;
		if (!T)   T = p;  // 是第一棵生成树的根(T的根)
		else // 其它生成树的根(前一棵的根的“兄弟”)
                   q->nextsibling = p; 
		q = p;             // q指示当前生成树的根
		DFSTree(G, v, p);  // 建立以p为根的生成树
	 }  //if
}  // DFSForest

2.最小生成树

1.Prim算法(生成树不唯一)

(1)基本思想:找最小的,最后每个顶点都被找到(按照找到点的顺序,不能任意跳)

记录从顶点集U到V-U的代价最小的边的辅助数组定义:
struct {
              VertexType  adjvex;
              VRType     lowcost;
           } closedge[MAX_VERTEX_NUM];


用普里姆算法从第u个顶点出发构造网G的最小生成树T,输出T的各条边:
void MiniSpanTree_PRIM(MGraph G, VertexType u) 
{  int i,j,k;
   k = LocateVex ( G, u );

   for ( j=0; j<G.vexnum; ++j )
                                                    // 辅助数组初始化
       if (j!=k) 
       {   closedge[j].adjvex=u;  
            closedge[j].lowcost=G.arcs[k][j].adj; 
       }
     closedge[k].lowcost = 0; 
for (i=1; i<G.vexnum; ++i) 

{  // 选择其余G.vexnum-1个顶点
    k = minimum(closedge);  // 求出T的下一个结点:第k顶点
    printf(closedge[k].adjvex, G.vexs[k]);// 输出生成树的边
    closedge[k].lowcost = 0;    // 第k顶点并入U集
    for (j=0; j<G.vexnum; ++j)
      if (G.arcs[k][j].adj < closedge[j].lowcost) 
      { 
         // 新顶点并入U后重新选择最小边
        closedge[j].adjvex=G.vexs[k];
        closedge[j].lowcost=G.arcs[k][j].adj;
      }
  }
} // MiniSpanTree

2.Kruskal算法(生成树不唯一)

(1)基本思想:找权值小的且不构成回路的。(可以任意挑)

#define INFINITY MAX_VAL /* 最大值∞ */
#define MAX_VEX 30 /* 最大顶点数 */
#define MAX_EDGE 100 /* 最大边数 */

typedef struct MSTEdge
{ int vex1 , vex2 ; /* 边所依附的两个顶点 */
WeightType weight ; /* 边的权值*/
} MSTEdge ; /* 边表元素类型定义 */

typedef struct
{ int vexnum , edgenum ; /* 顶点数和边数 */
VexType vexlist[MAX_VEX] ; /* 顶点表 */
MSTEdge edgelist[MAX_EDGE] ; /* 边表 */
}ELGraph ;


MSTEdge *Kruskal_MST(ELGraph *G)
/* 用Kruskal算法构造图G的最小生成树 */
{ MSTEdge TE[] ;
int j, k, v, s1, s2, Vset[] ;  WeightType  w ;
Vset=(int *)malloc(G->vexnum*sizeof(int)) ;
for (j=0; j<G->vexnum; j++)
Vset[j]=j ; /* 初始化数组Vset[n] */
sort(G->edgelist) ; /* 对表按权值从小到大排序 */
j=0 ; k=0 ;
while (j<G->vexnum-1&&k< G->edgenum)
{ s1=Vset[G->edgelist[j].vex1] ;
  s2=Vset[G->edgelist[j].vex2] ;
/* 若边的两个顶点的连通分量编号不同, 边加入到TE中 */
if (s1!=s2)
    { TE[k].vex1=G->edgelist[j].vex1 ;
       TE[k].vex2=G->edgelist[j].vex2 ;
       TE[k].weight=G->edgelist[j].weight ;
        k++ ;
        for (v=0; v<G->vexnum; v++)
             If (Vset[v]==s2)     Vset[v]=s1 ;
      }
    j++ ;
}
free(Vset) ;
return(TE) ;
}  /* 求最小生成树的Kruskal算法 */

 

五、有向无环图及其应用

1.拓扑排序 

(1)DAG:有向无环图;

(2)从有向图中选取一个没有前驱的顶点,      (入度为0 的顶点),并输出之;

从有向图中删去此顶点以及所有以它为尾的弧

重复上述步骤

 (3)拓扑排序算法描述

 算法:PPT111

(4)逆拓扑排序:选取没有后继的顶点删除此顶点以及所有以他为头的弧

2.关键路径

(1)AOV网:顶点表示活动,弧表示活动间优先关系,带权有向无环图。可用来估计工程完成的时间。

(2)关键路径:完成整个工程所需的时间取决于从开始点到结束点的最长路径长度,此长度最大的路径叫做关键路径。算法时间复杂度O(n+e)

六、最短路径

(1)Dijkstra算法 O (n^2):

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值