任务9城市间公路网建设方案的选择.docx

上传人:b****7 文档编号:11324810 上传时间:2023-02-26 格式:DOCX 页数:30 大小:43.50KB
下载 相关 举报
任务9城市间公路网建设方案的选择.docx_第1页
第1页 / 共30页
任务9城市间公路网建设方案的选择.docx_第2页
第2页 / 共30页
任务9城市间公路网建设方案的选择.docx_第3页
第3页 / 共30页
任务9城市间公路网建设方案的选择.docx_第4页
第4页 / 共30页
任务9城市间公路网建设方案的选择.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

任务9城市间公路网建设方案的选择.docx

《任务9城市间公路网建设方案的选择.docx》由会员分享,可在线阅读,更多相关《任务9城市间公路网建设方案的选择.docx(30页珍藏版)》请在冰豆网上搜索。

任务9城市间公路网建设方案的选择.docx

任务9城市间公路网建设方案的选择

任务9城市间公路网建设方案的选择

教学目标

1、知识目标

1)了解图的有关概念;

2)掌握图的邻接矩阵和邻接表的存储表示方法;

3)掌握图的遍历,深度优先搜索遍历和广度优先搜索遍历的算法;

4)掌握最小生成树的求解过程和算法。

2、能力目标

1)具有恰当的选择图作为数据的逻辑结构的能力;

2)具有应用图解决实际问题的能力。

3、素质目标

养成善于思考解决实际问题的良好习惯。

一、任务描述

有6个城市(A、B、C、D、E、F)如图9.1所示,已知每对城市间公路的建造费用,要求建造一个连接6个城市的交通网,使得任意两个城市间都可以直接或间接互达,要求使总的费用最小。

问:

如何建造6个城市间的公路网?

 

3

图9.1

二、相关知识

(一)图的基本概念

1、图(Graph)是由非空的顶点集合和一个描述顶点之间关系――边(或者弧)的集合组成,其形式化定义为:

G=(V,E)

其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合,集合E中P(vi,vj)表示顶点vi和顶点vj之间有一条直接连线,即偶对(vi,vj)表示一条边。

图9.2中G1给出了一个图的示例,在该图中:

集合V={v1,v2,v3,v4,v5};

集合E={(v1,v2),(v1,v4),(v2,v3),(v3,v4),(v3,v5),(v2,v5)}。

V2

V2

V1

V1

V3

V4

V3

V4

V5

图9.2无向图G1图9.2有向图G2

2、无向图。

在一个图中,如果任意两个顶点构成的偶对(vi,vj)∈E是无序的,即顶点之间的连线是没有方向的,则称该图为无向图。

如图9.2所示图G1是一个无向图。

3、有向图。

在一个图中,如果任意两个顶点构成的偶对(vi,vj)∈E是有序的,即顶点之间的连线是有方向的,则称该图为有向图。

如图9.2所示图G2是一个有向图:

G2=(V2,E2)

V2={v1,v2,v3,v4}

E2={,,,}

4、顶点、边、弧、弧头、弧尾。

图中,数据元素vi称为顶点(vertex);P(vi,vj)表示在顶点vi和顶点vj之间有一条直接连线。

如果是在无向图中,则称这条连线为边;如果是在有向图中,一般称这条连线为弧。

边用顶点的无序偶对(vi,vj)来表示,称顶点vi和顶点vj互为邻接点,边(vi,vj)依附于顶点vi与顶点vj;弧用顶点的有序偶对来表示,有序偶对的第一个结点vi被称为始点(或弧尾),在图中就是不带箭头的一端;有序偶对的第二个结点vj被称为终点(或弧头),在图中就是带箭头的一端。

5、无向完全图。

在一个无向图中,如果任意两顶点都有一条直接边相连接,则称该图为无向完全图。

可以证明,在一个含有n个顶点的无向完全图中,有n(n-1)/2条边。

6、有向完全图。

在一个有向图中,如果任意两顶点之间都有方向互为相反的两条弧相连接,则称该图为有向完全图。

在一个含有n个顶点的有向完全图中,有n(n-1)条边。

7、稠密图、稀疏图。

若一个图接近完全图,称为稠密图;称边数很少的图为稀疏图。

8、顶点的度、入度、出度。

顶点的度(degree)是指依附于某顶点v的边数,通常记为TD(v)。

在有向图中,要区别顶点的入度与出度的概念。

顶点v的入度是指以顶点为终点的弧的数目。

记为ID(v);顶点v出度是指以顶点v为始点的弧的数目,记为OD(v)。

有TD(v)=ID(v)+OD(v)。

例如,在G1中有:

TD(v1)=2TD(v2)=3TD(v3)=3TD(v4)=2TD(v5)=2

在G2中有:

ID(v1)=1OD(v1)=2TD(v1)=3

ID(v2)=1OD(v2)=0TD(v2)=1

ID(v3)=1OD(v3)=1TD(v3)=2

ID(v4)=1OD(v4)=1TD(v4)=2

n

i=1

可以证明,对于具有n个顶点、e条边的图,顶点vi的度TD(vi)与顶点的个数以及边的数目满足关系:

2e=(

9、边的权、网图。

与边有关的数据信息称为权(weight)。

在实际应用中,权值可以有某种含义。

比如,在一个反映城市交通线路的图中,边上的权值可以表示该条线路的长度或者等级;对于一个电子线路图,边上的权值可以表示两个端点之间的电阻、电流或电压值;对于反映工程进度的图而言,边上的权值可以表示从前一个工程到后一个工程所需要的时间等等。

边上带权的图称为网图或网络(network)。

如图9.3所示,就是一个无向网图。

如果边是有方向的带权图,则就是一个有向网图。

10、路径、路径长度。

顶点vp到顶点vq之间的路径(path)是指顶点序列vp,vi1,vi2,…,vim,vq.。

其中,(vp,vi1),(vi1,vi2),…,(vim,.vq)分别为图中的边。

路径上边的数目称为路径长度。

图9.2所示的无向图G1中,v1→v4→v3→v5与v1→v2→v5是从顶点v1到顶点v5的两条路径,路径长度分别为3和2。

11、回路、简单路径、简单回路。

称vi的路径为回路或者环(cycle)。

序列中顶点不重复出现的路径称为简单路径。

在图9.2G1中,前面提到的v1到v5的两条路径都为简单路径。

除第一个顶点与最后一个顶点之外,其他顶点不重复出现的回路称为简单回路,或者简单环。

如图9.2G2中的v1→v3→v4→v1。

12、子图。

对于图G=(V,E),G’=(V’,E’),若存在V’是V的子集,E’是E的子集,则称图G’是G的一个子图。

图9.4示出了G2和G1的两个子图G’和G’’。

8

5

V1

V2

V1

B

A

2

3

V3

7

V5

V4

V4

V3

D

C

(a)G’(b)G’’

图9.3一个无向网图示意图9.4图G2和G1的两个子图示意

C

B

A

V2

V1

B

A

C

D

D

F

E

V4

V3

F

E

(a)无向图G3(b)G3的两个连通分量

图9.5无向图及连通分量示意图9.6有向图G2的两个强连通分量示意

12、连通的、连通图、连通分量。

在无向图中,如果从一个顶点vi到另一个顶点vj(i≠j)有路径,则称顶点vi和vj是连通的。

如果图中任意两顶点都是连通的,则称该图是连通图。

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

图9.5(a)中有两个连通分量,如图9.5(b)所示。

13、强连通图、强连通分量。

对于有向图来说,若图中任意一对顶点vi和vj(i≠j)均有从一个顶点vi到另一个顶点vj有路径,也有从vj到vi的路径,则称该有向图是强连通图。

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

图9.2G2中有两个强连通分量,分别是{v1,v2,v3}和{v4},如图9.6所示。

14、生成树。

所谓连通图G的生成树,是G的包含其全部n个顶点的一个极小连通子图。

它必定包含且仅包含G的n-1条边。

图9.4(b)G”示出了图9.2中G1的一棵生成树。

在生成树中添加任意一条属于原图中的边必定会产生回路,因为新添加的边使其所依附的两个顶点之间有了第二条路径。

若生成树中减少任意一条边,则必然成为非连通的。

15、生成森林。

在非连通图中,由每个连通分量都可得到一个极小连通子图,即一棵生成树。

这些连通分量的生成树就组成了一个非连通图的生成森林。

(二)图的存储结构

图的存储表示方法很多,这里介绍两种最常用的方法。

至于具体选择哪一种方法,主要取决于具体的应用和要施加的操作。

    为了适合用C语言描述,以下假定顶点序号从0开始,即n个顶点图G顶点集是V(G)={v0,v1,…,vn-1}。

1、图的邻接矩阵表示法

1)图的邻接矩阵

    设G=(V,E)是具有n个顶点的图,则G的邻接矩阵是元素具有如下性质的n阶方阵:

2)图的邻接矩阵表示法:

①用邻接矩阵表示顶点间的相邻关系;

②用一个顺序表来存储顶点信息。

 

0101

1011

0100

1100

A=

图9.7一个无向图的邻接矩阵表示

3)网络的邻接矩阵

    若G是网络,则邻接矩阵可定义为:

       

其中:

wij表示边上的权值;∞表示一个计算机允许的、大于所有边上权值的数。

0

96∞963∞

2

1

39∞45∞

4A=64∞∞7

5735∞∞8

3

8

4

∞∞78∞

图9.8一个网图的邻接矩阵表示

4)图的邻接矩阵存储结构的描述

#defineVertexNum20//最大顶点数

typedefcharVertexType;//顶点类型定义

typedefintEdgeType;//权值类型定义

typedefstruct{

VertexTypevexs[VertexNum];//顶点表

EdgeTypeedges[VertexNum][VertexNum];//邻接矩阵,可看作边表

intn,e;//图中当前的顶点数和边数

}MGraph;

5)建立无向网络的算法

/*建立无向网络的算法:

*/

voidCreateMGraph(MGraph*G)

{//建立无向网(图)的邻接矩阵表示

inti,j,k,w;

scanf("%d%d",&G->n,&G->e);//输入顶点数边数

getchar();

printf("读入顶点信息,建立顶点表:

");

for(i=0;in;i++)//读入顶点信息,建立顶点表

G->vexs[i]=getchar();

getchar();

for(i=0;in;i++)

for(j=0;jn;j++)

G->edges[i][j]=0;//邻接矩阵初始化

for(k=0;ke;k++){//读入e条边,建立邻接矩阵

scanf("%d%d%d",&i,&j,&w);//输入权w

G->edges[i][j]=w;

G->edges[j][i]=w;

}

}

2、图的邻接表表示法

1)图的邻接表

    对于图G中的每个顶点vi,把所有邻接于vi的顶点vj链成一个带头结点的单链表,这个单链表就称为顶点vi的邻接表(AdjacencyList)。

2)邻接表的结点结构

(1)表结点结构:

邻接表中每个表结点均有两个域

①邻接点域adjvex,存放与vi相邻接的顶点vj的序号j;

②链域next,将邻接表的所有表结点链在一起。

Ø注意:

若要表示边上的信息(如权值),则在表结点中还应增加一个数据域。

(2)头结点结构:

顶点vi邻接表的头结点包含两个域

①顶点域vertex,存放顶点vi的信息;

②指针域firstedge,vi的邻接表的头指针。

3)无向图的邻接表

    对于无向图,vi的邻接表中每个表结点都对应于与vi相关联的一条边。

因此,将邻接表的表头向量称为顶点表。

将无向图的邻接表称为边表。

序号vertexfirstedge

0

V0

13∧

V1

1

023∧

1

1

2

V2

V3

3

01∧

图9.9图的邻接表表示

Ø注意:

n个顶点e条边的无向图的邻接表表示中有n个顶点表结点和2e个边表结点。

4)有向图的邻接表

    对于有向图,vi的邻接表中每个表结点都对应于以vi为始点射出的一条边。

因此,将有向图的邻接表称为出边表。

Ø注意:

n个顶点e条边的有向图,它的邻接表表示中有n个顶点表结点和e个边表结点。

5)有向图的逆邻接表

   在有向图中,为图中每个顶点vi建立一个入边表的方法称逆邻接表表示法。

入边表中的每个表结点均对应一条以vi为终点(即射入vi)的边。

0

0

V1

V1

21∧3∧

0

1

V2

1

V2

2

V3

3

2

V3

3

V4

3

V4

0∧

0∧2∧

(a)邻接表(b)逆邻接表

图9.10图9.2中G2的邻接表和逆邻接表

6)图的邻接表存储结构的描述

图的邻接表存储结构的描述

typedefstructnode{//边表结点定义

intadjvex;//邻接点域

structnode*next;//链域

//若要表示边上的权,则应增加一个数据域

}EdgeNode;

typedefstructvnode{//顶点表结点定义

VertexTypevertex;//顶点域

EdgeNode*firstedge;//边表头指针

}VertexNode;

typedefVertexNodeAdjList[VertexNum];//AdjList是邻接表类型

typedefstruct{//邻接表定义

AdjListadjlist;//邻接表

intn,e;//图中当前顶点数和边数

}ALGraph;

7)建立无向图的邻接表算法

建立无向图的邻接表算法

voidCreateALGraph(ALGraph*G)

{//建立无向图的邻接表表示

inti,j,k;

EdgeNode*s;

scanf("%d%d",&G->n,&G->e);//读入顶点数和边数

getchar();

for(i=0;in;i++){//建立顶点表

G->adjlist[i].vertex=getchar();//读入顶点信息

G->adjlist[i].firstedge=NULL;//边表置为空表

}

for(k=0;ke;k++){//建立边表

scanf("%d%d",&i,&j);//读入边(vi,vj)顶点对序号

s=(EdgeNode*)malloc(sizeof(EdgeNode));//生成边表结点

s->adjvex=j;//邻接点序号为j

s->next=G->adjlist[i].firstedge;

G->adjlist[i].firstedge=s;//将新结点*s插入顶点vi的边表头部

s=(EdgeNode*)malloc(sizeof(EdgeNode));

s->adjvex=i;//邻接点序号为i

s->next=G->adjlist[j].firstedge;

G->adjlist[j].firstedge=s;//将新结点*s插入顶点vj的边表头部

}

}

(三)图的遍历

1、图的遍历:

从图的某个顶点出发,沿着某条搜索路径对图中每个顶点各做一次且仅做一次访问,这一过程称为图的遍历。

它是许多图的算法的基础。

深度优先遍历和广度优先遍历是最为重要的两种遍历图的方法。

它们对无向图和有向图均适用。

Ø注意:

①由于图中任一顶点都可能和其它顶点相邻接,所以在遍历图时,访问了某顶点之后,有可能顺着某条回路又回到了该顶点。

为了避免重复访问同一个顶点,必须记住每个已访问的顶点。

为此,可设一向量visited[0…n-1],该向量的每个元素的初值均为0,如果访问了顶点Vi,就将visited[i]置为1,这样便可通过visited[i]的值来标志顶点Vi是否被访问过。

②以下假定遍历过程中访问顶点的操作是简单地输出顶点。

2、图的深度优先遍历

假设给定图G的初态是所有顶点均未曾访问过。

在G中任选一顶点v为初始出发点(源点)。

①递归定义:

首先访问出发点v,并将其标记为已访问过;然后依次从v出发搜索v的每个邻接点w。

若w未曾访问过,则以w为新的出发点继续进行深度优先遍历,直至图中所有和源点v有路径相通的顶点(亦称为从源点可达的顶点)均已被访问为止。

若此时图中仍有未访问的顶点,则另选一个尚未访问的顶点作为新的源点重复上述过程,直至图中所有顶点均已被访问为止。

Ø说明:

图的深度优先遍历类似于树的前序遍历。

采用的搜索方法的特点是尽可能先对纵深方向进行搜索。

这种搜索方法称为深度优先搜索(Depth-FirstSearch)。

相应地,用此方法遍历图就很自然地称之为图的深度优先遍历。

②深度优先搜索的过程

    设x是当前被访问顶点,在对x做过访问标记后,选择一条从x出发的未检测过的边(x,y)。

若发现顶点y已访问过,则重新选择另一条从x出发的未检测过的边,否则沿边(x,y)到达未曾访问过的y,对y访问并将其标记为已访问过;然后从y开始搜索,直到搜索完从y出发的所有路径,即访问完所有从y出发可达的顶点之后,才回溯到顶点x,并且再选择一条从x出发的未检测过的边。

上述过程直至从x出发的所有边都已检测过为止。

此时,若x不是源点,则回溯到在x之前被访问过的顶点;否则图中所有和源点有路径相通的顶点(即从源点可达的所有顶点)都已被访问过,若图G是连通图,则遍历过程结束,否则继续选择一个尚未被访问的顶点作为新源点,进行新的搜索过程。

③深度优先遍历的递归算法(DFS算法) 

邻接矩阵表示的深度优先遍历算法

intvisited[VertexNum]={0};//定义标志向量

voidDFSM(MGraph*G,inti)

{//以vi为出发点进行搜索,设邻接矩阵是0,l矩阵

intj;

printf("%4c",G->vexs[i]);//访问顶点vi

visited[i]=1;

for(j=0;jn;j++)//依次搜索vi的邻接点

if((G->edges[i][j]==1)&&(!

visited[j]))

DFSM(G,j);//(vi,vj)∈E,且vj未访问过

}

voidDFSTraverse(MGraph*G)

{//深度优先遍历以邻接矩阵表示G

inti;

for(i=0;in;i++)

visited[i]=0;//标志向量初始化

for(i=0;in;i++)

if(!

visited[i])//vi未访问过

DFSM(G,i);//以vi为源点开始DFSM搜索

}

邻接表表示的深度优先搜索算法

intvisited[VertexNum]={0};//定义标志向量

voidDFS(ALGraph*G,inti)

{//以vi为出发点进行深度优先搜索

EdgeNode*p;

printf("%4c",G->adjlist[i].vertex);//访问顶点vi

visited[i]=1;//标记vi已访问

p=G->adjlist[i].firstedge;//取vi边表的头指针

while(p){//依次搜索vi的邻接点vj,这里j=p->adjvex

if(!

visited[p->adjvex])//若vi尚未被访问

DFS(G,p->adjvex);//则以vj为出发点向纵深搜索

p=p->next;//找vi的下一邻接点

}

}

voidDFSTraverse(ALGraph*G)

{//深度优先遍历以邻接表表示G

inti;

for(i=0;in;i++)

visited[i]=0;//标志向量初始化

for(i=0;in;i++)

if(!

visited[i])//vi未访问过

DFS(G,i);//以vi为源点开始DFS搜索

}

3、图的广度优先遍历

设图G的初态是所有顶点均未访问过。

在G中任选一顶点v为源点,则广度优先遍历可以定义为:

①递归定义:

首先访问出发点v,接着依次访问v的所有邻接点w1,w2,…,wt,然后再依次访问与wl,w2,…,wt邻接的所有未曾访问过的顶点。

依此类推,直至图中所有和源点v有路径相通的顶点都已访问到为止。

此时从v开始的搜索过程结束。

    若G是连通图,则遍历完成;否则,在图G中另选一个尚未访问的顶点作为新源点继续上述的搜索过程,直至G中所有顶点均已被访问为止。

Ø说明:

广度优先遍历类似于树的按层次遍历。

采用的搜索方法的特点是尽可能先对横向进行搜索,故称其为广度优先搜索(Breadth-FirstSearch)。

相应的遍历也就自然地称为广度优先遍历。

②广度优先搜索的过程

    在广度优先搜索过程中,设x和y是两个相继要被访问的未访问过的顶点。

它们的邻接点分别记为x1,x2,…,xs和y1,y2,…,yt。

    为确保先访问的顶点其邻接点亦先被访问,在搜索过程中使用FIFO队列来保存已访问过的顶点。

当访问x和y时,这两个顶点相继入队。

此后,当x和y相继出队时,我们分别从x和y出发搜索其邻接点x1,x2,…,xs和y1,y2,…,yt,对其中未访者进行访问并将其入队。

这种方法是将每个已访问的顶点入队,故保证了每个顶点至多只有一次入队。

③广度优先遍历的递归算法(BFS算法) 

邻接矩阵表示的广度优先遍历算法:

intvisited[VertexNum]={0};//定义标志向量

voidBFSM(MGraph*G,intk){//以vk为源点对用邻接矩阵表示的图G进行广度优先搜索

inti,j;

CirQueueQ;

InitQueue(&Q);

printf("%4c",G->vexs[k]);//访问源点vk

visited[k]=1;

EnQueue(&Q,k);

while(!

QueueEmpty(&Q)){

i=DeQueue(&Q);//vi出队

for(j=0;jn;j++)//依次搜索vi的邻接点vj

if(G->edges[i][j]==1&&!

visited[j]){//vi未访问

printf("%4c",G->vexs[j]);//访问vi

visited[j]=1;

EnQueue(&Q,j);//访问过的vi入队

}

}

}

voidDFSTraverse(MGraph*G){//广度优先遍历以邻接矩阵表示G

inti;

for(i=0;in;i++)

visited[i]=0;//标志向量初始化

for(i=0;in;i++)

if(!

visited[i])//vi未访问过

BFSM(G,i);//以vi为源点开始DFSM搜索

}

邻接表表示的广度优先遍历算法:

intvisited[VertexNum]={0};//定义标志向量

voidBFS(ALGraph*G,intk)

{//以vk为源点对用邻接表表示的图G进行广度优先搜索

inti;

CirQueueQ;//须将队列定义中DataType改为int

EdgeNode*p;

InitQueue(&Q);//队列初始化

//访问源点vk

printf("%4c",G->adjlist[k].vertex);

visited[k]=1;

EnQueue(&Q,k);//vk已访问,将其入队。

(实际上是将其序号入队)

while(!

QueueEmpty(&Q)){//队非空则执行

i=DeQueue(&Q);//相当于vi出队

p=G->

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 节日庆典

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1