第六章 图

一、图的定义和基本术语

1.图的定义

图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:

                          G=(V,E)

其中G表示一个图,V是图G中顶点的集合,E是图G顶点之间边的集合。

如果图的任意两个顶点之间的边都是没有方向的,则称该图为无向图,否则为有向图。

2.图的基本术语

简单图:在图中,若不存在顶点到其自身的边,且同一条边部重复出现。

邻接、依附:在无向图中,任意两个顶点有边相连接,则互为邻接点,同时称边依附于顶点。

无向完全图、有向完全图

在无向完全图中,任意两个顶点都有边;

含n个节点的无向完全图有n*(n-1)/2条边。

有向图中,任意两顶点都有方向互相相反的两条弧;

含有n*(n-1)条边。

稠密图、稀疏图:称边少的为稀疏图,反之多的就是稠密图。(相对而言)

权、网:权通常是指对边赋予有意义的数量值。

边上带权的图称为网。

路径、路径长度、回路

路径长度=结点到根的边的个数;

第一个顶点和最后一个顶点有相同的路径称为回路;

简单路径、简单回路

简单路径:在路径序列中,顶点部重复出现。

简单回路:除了第一个顶点和最后一个顶点之外,其余顶点部重复出现。

子图:图G=(V,E)和G’=(V’,E’),如果V’属于V且E’属于E,则称G’是G的子图。

连通图、连通分量

连通图:在无向图中,若任意顶点之间有路径;

连通分量:非连通图的极大连通子图

强连通图、强连通分量

强连通图:在有向图中,对任意顶点i、j,若从顶点i到j均有路径;

强连通分量:非连通图的极大强连通子图。

生成树、生成森林:具有n个顶点的连通图G的生成树是包含G中全部顶点的一个极小连通子图。生成树可以任意指定一顶点为数的根。在生成树添加一条原图中的边,则必会产生回路,若减少一条边,则会成为非连通。所以一棵具有n个顶点的生成树有且仅有n-1条边。

6.1.2 图的抽象数据类型定义

图是一种与具体应用密切相关的数据结构,它的基本操作往往随应用不同而有很大差别。下面给出一个图的抽象数据类型定义的例子,简单起见,基本操作仅包含图的遍历,针对具体应用,需要重新定义其基本操作。 

ADT  Graph

Data

    顶点的有穷非空集合和边的集合

Operation

InitGraph

     前置条件:图不存在

     输入:无 

     功能:图的初始化

     输出:无

     后置条件:构造一个空的图

DestroyGraph 

     前置条件:图已存在

     输入:无 

     功能:销毁图

     输出:无

     后置条件:释放图所占用的存储空间

DFSTraverse

     前置条件:图已存在

     输入:遍历的起始顶点v

     功能:从顶点v出发深度优先遍历图

     输出:图中顶点的一个线性排列

     后置条件:图保持不变

  BFSTraverse

     前置条件:图已存在

     输入:遍历的起始顶点v

     功能:从顶点v出发广度优先遍历图

     输出:图中顶点的一个线性排列

     后置条件:图保持不变

endADT

6.1.3 图的遍历操作

图的遍历操作是图中最基本的操作。是指从图中某一顶点出发,对图中所有顶点访问一次且仅访问一次。

图遍历中关键问题:

(1)在图中没有一个确定的开始结点任一个都可以作为开始结点;

解决方案:从编号小的顶点开始 。

(2)从某个顶点出发可能到达不了所有其他顶点;

解决方案:多次调用从某顶点出发遍历图的算法。

(3)由于可能存在回路,某些顶点可能被重复访问;

解决方案:附设访问标志数组visited[n] 

(4)在图中,一个顶点可以与其它多个顶点相邻接,当这样的顶点访问过后,如何选取下一个要访问的结点?解决方案:深度优先遍历和广度优先遍历。

1.深度优先遍历

类似于树的前序遍历

基本思想:

(1) 访问顶点v;

(2) 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;

(3) 重复以上两步,直到所有路径都被访问到。

2.广度优先遍历

类似于层序遍历

(1) 访问顶点v;

(2) 依次访问v的各个未被访问的邻接点v1,v2,……vk;

(3) 分别从v1,v2,……vk出发依次访问他们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问结点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。

二、图的存储结构及实现

一个图包括两部分信息:信息以及描述顶点之间关系的信息。

6.2.1邻接矩阵(数组表示法)

基本思想:用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。

假设图G=(VE)有n个顶点,则邻接矩阵是一个n×n的方阵,定义为:

 

arc[i][j]={1        若(vivj)∈E(或<vivj>∈E)

0        其它

1.邻接矩阵构造函数算法

template<class dt>

MGraph<dt>::MGraph(dt a[],int n,int e)

{

vertexnum=n;arcnum=e;

for(i=0;i<vertexnum;i++)

vertex[i]=a[i];

for(i=0;i<vertexnum;i++)

for(j=0;j<vertexnum;j++)

arc[i][j]=0;

for(k=0;k<arcnum;k++)

{

cin>>i>>j;

arc[i][j]=1;

}

}

2.深度优先遍历算法

template<class dt>

void MGraph<dt>::DFSTraverse(int v)

{

cout<<vertex[v];visited=1;

for(j=0;j<vertexnum;j++)

    if(arc[v][j]==1&&visited[j]==0) DFSTraverse(j);

}

3.广度优先遍历算法

template<class dt>

void MGraph<dt>::BFSTraverse(int v)

{

front=rear=-1;

cout<<vertex[v];visited[v]=1;Q[++rear]=v;

while(front!=rear)

{

v=Q[++front];

for(j=0;j<vertexnum;j++)

if(arc[v][j]==1&&visited[j]==0)

{

cout<<vertex[j];visited[j]=1;

Q[++rear]=j;

}

}

}

6.2.2 邻接表

1.邻接表:一种顺序存储与链接存储相结合的存储方法,类似于树的孩子链表表示法。

2.邻接表构造函数算法:

template<class dt>

ALGraph<dt>::ALGraph(dt a[],int n,int e)

{

vertexnum=n;arcnum=e;

for(i=0;i<vertexnum;i++)

{

adjlist[i].vertex=a[i];

adjlist[i].firstedge=NULL;

}

for(k=0;k<arcnum;k++)

{

cin>>i>>j;

s=new Arcnode;s->adjvex=j;

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

adjlist[i].firstedge=s;

}

}

3.广度优先遍历算法

template<class dt>

void ALGraph<dt>::BFSTraverse(int v)

{

front=rear=-1;

cout<<adjlist[v].vertex;visited[v]=1;Q[++rear]=v;

while(front!=rear)

v=Q[++front];

p=adjlist[v].firstarc;

while(p!=NULL)

{

j=p->adjlist;

if(visited[j]==0)

{

cout<<adjlist[j].vertex;visited[j]=1;Q[++rear]=j;

}

p=p->next;

}

}

4.深度优先遍历算法

template<class dt>

void ALGraph<dt>::DFSTraverse(int v)

{

cout<<adjlist[v].vertex;visited=1;

p=adjlist[v].firstedge;

while(p!=NULL)

{

j=p->adjvex;

if(visited[j]==0) DFSTraverse(j);

p=p->next;

}

}

6.3 最小生成树

6.3.1 MST 性质

假设G=(V,E)是一个无向连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u,v)的最小生成树。

6.3.2 Prim 算法

void Prim(MGraph G)

{

for(i=1;i<G.vertexnum;i++)

{

shortedge[i].lowcost=G.arc[0][i];

shortedge.adjvex=0;

}

shortedge[0].lowcost=0;

for(i=1;i<G.vertexnum;i++)

{

k=minedge(shortedge,G.vertexnum)

cout<<"("<<k<<shortedge[k].adjvex<<")"<<shortedge[k].lowcost;

shortedge[k].lowcost=0;

for(j=1;j<G.vertexnum;j++)

if G.arc[k][j]<shortedge[j].lowcost

{

shortedge[j].lowcost=G.arc[k][j];

shortedge[j].adjvex=k;

}

}

}

2.最小生成树算法 Kruskal

void Kruskal(EdgeGraph)

{

for(i=0;i<G.vertexnum;i++)

parent[i]=-1;

for(num=0,i=0;i<G.edgenum;i++)

{

vex1=Findroot(parent,G.edge[i].from);

vex2=Findroot(parent,G.edge[i].to);

if(vex1!=vex2)

{

cout<<"("<<G.edge[i].from<<G.edge[i].to<<")"<<endl;

parent[ver2]=ver1;

num++;

if(num==n-1) return;

}

}

}

int Findroot(int parent[],int v)

{

t=v;

if(parent[t]>-1) t=parent[t];

return t;

}

6.4最短路径

1.最短路径:两顶点之间经历的边数最少的路径(路径上的第一个顶点称为源点,最后一个顶点称为终点)。

2.Dijkstra 算法:

void Dijkstra(MGraph G,int v)

{

for(i=0;i<G.vertexnum;i++)

{

dist[i]=G.arc[v][i];

if(dist[i]!=∞) path[i]=G.vertex[v]+G.vertex[i];

else path[i]=" ";

}

s[0]=v;dist[v]=0;num=1;

while(num<G.vertexnum)

{

for(k=0,i=0;i<G.vertexnum;i++)

if((dist[i]!=0)&&(dist[i]<dist[k])) k=i;

cout<<dist[k]<<path[k];

s[num++]=k;

for(i=0;i<G.vertexum;i++)

if(dist[i]>dist[k]+G.arc[k][i])

{

dist[i]=dist[k]+G.arc[k][j];

path[i]=path[k]+G.vertex[i];

}

dist[k]=0;

}

}

猜你喜欢

转载自blog.csdn.net/jklimingzhi/article/details/41849327