图的存储遍历及最小生成树.docx

上传人:b****8 文档编号:13194719 上传时间:2023-06-11 格式:DOCX 页数:20 大小:241.88KB
下载 相关 举报
图的存储遍历及最小生成树.docx_第1页
第1页 / 共20页
图的存储遍历及最小生成树.docx_第2页
第2页 / 共20页
图的存储遍历及最小生成树.docx_第3页
第3页 / 共20页
图的存储遍历及最小生成树.docx_第4页
第4页 / 共20页
图的存储遍历及最小生成树.docx_第5页
第5页 / 共20页
图的存储遍历及最小生成树.docx_第6页
第6页 / 共20页
图的存储遍历及最小生成树.docx_第7页
第7页 / 共20页
图的存储遍历及最小生成树.docx_第8页
第8页 / 共20页
图的存储遍历及最小生成树.docx_第9页
第9页 / 共20页
图的存储遍历及最小生成树.docx_第10页
第10页 / 共20页
图的存储遍历及最小生成树.docx_第11页
第11页 / 共20页
图的存储遍历及最小生成树.docx_第12页
第12页 / 共20页
图的存储遍历及最小生成树.docx_第13页
第13页 / 共20页
图的存储遍历及最小生成树.docx_第14页
第14页 / 共20页
图的存储遍历及最小生成树.docx_第15页
第15页 / 共20页
图的存储遍历及最小生成树.docx_第16页
第16页 / 共20页
图的存储遍历及最小生成树.docx_第17页
第17页 / 共20页
图的存储遍历及最小生成树.docx_第18页
第18页 / 共20页
图的存储遍历及最小生成树.docx_第19页
第19页 / 共20页
图的存储遍历及最小生成树.docx_第20页
第20页 / 共20页
亲,该文档总共20页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

图的存储遍历及最小生成树.docx

《图的存储遍历及最小生成树.docx》由会员分享,可在线阅读,更多相关《图的存储遍历及最小生成树.docx(20页珍藏版)》请在冰点文库上搜索。

图的存储遍历及最小生成树.docx

图的存储遍历及最小生成树

图的存储、遍历及最小生成树

图的定义

图(Graph)是由一个用线或边连接在一起的顶点或节点的集合。

是一种比线性表和树更为复杂的非线性数据结构,可称为图状结构或网状结构,前面讨论的线性表和树都可以看成是图的简单情况。

    图G由两个集合V和E组成,记为:

       G=(V,E)

  其中:

  V是顶点的有穷非空集合,

  E是V中顶点偶对(称为边)的有穷集。

    通常,也将图G的顶点集和边集分别记为V(G)和E(G)。

E(G)可以是空集。

若E(G)为空,则图G只有顶点而没有边。

有向图和无向图

1.有向图

    如图7.1G2所示每条边都是有方向的,则称G为有向图(Digraph)。

            

(1)有向边的表示

    有向边又称为弧,通常用尖括弧表示一条有向边,表示从顶点v到w的一段弧,v称为边的始点(或尾顶点),w称为边的终点,(或头顶点),代表两条不同的弧。

 【例】表示一条有向边,vi是边的始点(起点),vj是边的终点。

注意:

是两条不同的有向边。

(2)有向图的表示

 【例】上面7.1图中G2是一个有向图。

图中边的方向是用从始点指向终点的箭头表示的,该图的顶点集和边集分别为:

顶点集V={v1,v2,v3}

弧集E={,,,}。

注意:

表示两条不同的边。

2.无向图

    如图7.1G1中所示的每条边都是没有方向的,则称G为无向图(Undigraph)。

(1)无向边的表示

    通常用圆括号表示无向边,(v,w)表示顶点v和w间相连的边。

在无向图中(v,w)和(w,v)表示同一条边,如果顶点v,w之间有边(v,w),则v,w互称为邻接点。

 【例】无序对(vi,vj)和(vj,vi)表示同一条边。

(2)无向图的表示

 【例】上面7.1图中的G1是无向图,它们的顶点集和边集分别为:

顶点集:

V={v1,v2,v3,v4}

边集:

E={(v1,v2),(v1,v3),(v1,v4),(v2,v3),(v3,v4)}

注意:

(v2,v1)与(v1,v2)表示同一条边,(v2,v3)与(v3,v2)也表示同一条边,等等。

3.图G的顶点数n和边数e的关系

(1)若G是无向图,则0≤e≤n(n-1)/2

    恰有n(n-1)/2条边的无向图称无向完全图(Undireet-edCompleteGraph)

(2)若G是有向图,则0≤e≤n(n-1)。

    恰有n(n-1)条边的有向图称为有向完全图(DirectedCompleteGraph)。

 注意:

    完全图具有最多的边数。

任意一对顶点间均有边相连。

4.对图的讨论中我们对图作一些限制:

第一,图中不能有从顶点自身到自身的边(即自身环),就是说不应有形如(vx,vx)的边或的弧。

第二,两个顶点vt和vu之相关联的边不能多于一条。

图的邻接矩阵

1.图的邻接矩阵表示法

    在图的邻接矩阵表示法中:

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

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

2.图的邻接矩阵(AdacencyMatrix)

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

     

【例】下图中无向图G5和有向图G6的邻接矩阵分别为Al和A2。

从图的邻接矩阵表示法中可以得到如下结论:

  

(1)对于n个顶点的无向图,有A(i,i)=0,1≤i≤n。

(2)无向图的邻接矩阵是对称的,即A(i,j)=A(j,i),1≤i≤n,1≤j≤n。

(3)有向图的邻接矩阵不一定对称的。

因此用邻接矩阵来表示一个具有n个顶点的有向图时需要n2个单位来存储邻接矩阵;对有n个顶点的无向图则需存入上(下)三角形,故只需n(n+1)/2个单位。

(4)无向图的邻接矩阵的第i行(或第i列)非零元素的个数正好是第i个顶点的度TD(vi)。

(5)有向图的邻接矩阵的第i行(或第i列)非零元素的个数正好是第i个顶点的出度OD(vi)[或入度ID(vi)]。

 

3.网(带权值的图)的邻接矩阵

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

     

  

   其中:

     wij表示边上的权值;

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

【例】下面(a)是一个带权图,(b)是对应的邻接矩阵的存储结构

       (a)带权图                       (b)邻接矩阵

4.邻接矩阵的图类

constintMaxVertices=10;

constintMaxWeight=32767;

classAdjMWGraph

 {private:

  intVertices[10];      //顶点信息的数组

  intEdge[MaxVertices][MaxVertices];    //边的权值信息的矩阵

  intnumE;      //当前的边数

  intnumV;      //当前的顶点数

  public:

………;     //公有函数

  private:

………;     //私有函数

 }

 注意:

    ①在简单应用中,可直接用二维数组作为图的邻接矩阵(顶点表及顶点数等均可省略)。

    ②当邻接矩阵中的元素仅表示相应的边是否存在时,EdgeTyPe可定义为值为0和1的枚举类型。

    ③无向图的邻接矩阵是对称矩阵,对规模特大的邻接矩阵可压缩存储。

    ④邻接矩阵表示法的空间复杂度S(n)=0(n2)。

图的邻接链表

邻接链表(AdjacencyList)是图的一种链式存储结构,与树型结构中的孩子链表相似。

通常邻接链表也称邻接表。

1.邻接表的结点结构

边结点结构

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

 ①邻接点域adjvex

  存放与vi相邻接的顶点vj的序号j。

 ②链域next

  将邻接表的所有表结点链在一起。

 注意:

    如果带权图,则在表结点中还应增加一个保存权值等相关信息info。

2.邻接表的表示

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

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

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

 注意:

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

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

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

 注意:

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

4.有向图的逆邻接表

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

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

【例】

注意:

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

图的遍历

从图中某一顶点出发,按某种搜索方法访遍其余顶点,且使每一顶点仅被访问一次。

这一过程称为图的遍历。

  遍历图的基本搜索方法有两种:

深度优先搜索DFS(DepthFirstSearch)和广度优先搜索BFS(BroadFirstSearch)。

这两种方法都适用于有向图和无向图。

图的遍历算法设计需要考虑3个问题:

  

(1)图的特点没有首尾之分,所以算法的参考要指定访问的第一个顶点。

  

(2)对图的遍历路径有可能构成一个回路,从而造成死循环,所以算法设计要考虑遍历路径可能的回路问题。

  (3)一个顶点可能和若干个顶点都是想邻顶点,要使一个顶点的所有想邻顶点按照某种次序被访问。

  对于连通图,从初始顶点出发一定存在路径和图中的所有其他顶点相连,所以对于连通图从初始顶点出发一定可以遍历该图。

图的深度优先遍历

  图的深度优先遍历DFS算法是每次在访问完当前顶点后,首先访问当前顶点的一个未被访问过的邻接顶点,然后去访问这个邻接点的一个未被访问过的邻接点,这样的算法是一个递归算法。

1.连通图的深度优先遍历算法思想。

(1)访问初始顶点v并标记顶点v已访问。

(2)查找顶点v的第一个邻接顶点w。

(3)若顶点v的邻接顶点w存在,则继续执行;否则回溯到v,再找v的另外一个未访问过的邻接点。

(4)若顶点w尚未被访问,则访问顶点w并标记顶点w为已访问。

(5)继续查找顶点w的下一个邻接顶点wi,如果v取值wi转到步骤(3)。

直到连通图中所有顶点全部访问过为止。

【例】现以图7.9(a)为例说明深度过优先搜索过程。

假定v1是出发点,首先访问v1。

因v1有两个邻接点v2、v3均未被访问过,可以选择v2作为新的出发点,访问v2之后,再找v2的未访问过的邻接点。

同v2邻接的有v1、v4、v5,其中v1已被访问过,而v4、v5尚未被访问过,可以v4选择作为新的出发点。

重复上述搜索过程,继续依次访问v8、v5。

访问v5之后,由于与v5相邻的顶点均已被访问过,搜索退回到v8。

由于v8、v4、v2都是已被访问的邻接点,所以搜索过程连续地从v8退回到v4,再退回到v2,最后退回到v1。

这时选择v1的未被访问过的邻接点v3,继续往下搜索,依次访问v3、v6、v7,从而遍历了图中全部顶点。

在这个过程中得到的顶点的访问序列为:

          

       

具体算法演示请点击查看

2.深度优先遍历算法

(1)邻接矩阵的深度优先遍历算法:

【算法7.1】

voidAdjMWGraph:

:

Depth(intv,intvisited[])

{cout<<""\n顶点"<

"<

visited[v]=1;

for(intcol=0;col

{if(Edge[v][col]==0||Edge[v][col]==MaxWeight)continue;

if(!

visited[col])Delpth(col,visited);

}

}

  故用邻接矩阵表示图时,搜索一个顶点的所有邻接点需花费O(n)时间,则从n个顶点出发搜索的时间应为O(n2),即DFS算法的时间复杂度是(n2)。

(2)邻接链表的深度优先遍历算法:

【算法7.2】

VoidAdjTWGraph:

:

DepthFirst(intv,intvisited[])

{intvj;Edge*p;

cout<

visited[v]=1;

p=Vertices[v].adj;//取v的邻接边结点

while(p!

=NULL)

{vj=p->dest;//取v的邻接点编号

if(visited[vj]==0)DepthFirst(vj,visited);

p=p->next;//取下一个邻接边结点

}

}

  使用邻接链表来表示图时,其DFS算法的时间复杂度为O(n+e),此处e为无向图中边的数目或有向图中弧的数目。

图的广度优先遍历

  图的广度优先遍历BFS算法是一个分层搜索的过程,和树的层序遍历算法类同,它也需要一个队列以保持遍历过的顶点顺序,以便按出队的顺序再去访问这些顶点的邻接顶点。

1.连通图的广度优先遍历算法思想。

(1)顶点v入队列。

(2)当队列非空时则继续执行,否则算法结束。

(3)出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。

(4)查找顶点v的第一个邻接顶点col。

(5)若v的邻接顶点col未被访问过的,则col入队列。

(6)继续查找顶点v的另一个新的邻接顶点col,转到步骤(5)。

直到顶点v的所有未被访问过的邻接点处理完。

转到步骤

(2)。

【例】下面以图(a)为例说明广度优先搜索的过程。

首先从起点v1出发访问v1。

v1有两个未曾访问的邻接点v2和v3。

先访问v2,再访问v3。

然后再先访问v2的未曾访问过的邻接点v4、v5及v3的未曾访问过的邻接v6和v7,最后访问v4的未曾访问过的邻接点v8。

至此图中所有顶点均已被访问过。

得到的顶点访问序列为:

          

       

具体算法演示请点击查看

2.广度优先遍历算法

(1)邻接矩阵的广度优先遍历算法:

【算法7.1】

voidAdjMWGraph:

:

Depth(intv,intvisited[])

 { sqQueue< int>q;    //定义队列queue

  q.EnQueue(v);    //顶点v入队列

  while(!

q.IsEmpty())    //当队列非空时循环

    { v=q.DeQueue();    //出队列得到队头顶点u

     cout<

"<

     visited[col]=1;//标记顶点v已访问

     for(intcol=0;col

     if(Edge[v][col]>0;&&Edge[v][col]

     q.EnQueue(col);

  }

  cout<

"<<"endl;

 };

  邻接矩阵表示图时,搜索一个顶点的所有邻接点需花费O(n)时间,则从n个顶点出发搜索的时间应为O(n2),即BFS算法的时间复杂度是(n2)。

(2)邻接链表的深度优先遍历算法:

【算法7.2】

voidAdjTWGraph:

:

BroadFirst(intv,intvisited[])

 { intvj; Edge*p; SqQueueQ;Q.EnQueue(v);

  while(!

Q.IsEmpty())       //队列不空,循环

   { v=Q.DeQueue();    //出队并且访问顶点v

   cout<

   p=Vertices[v].adj;      //取v的邻接边结点

  while(p!

=NULL) {vj=p->dest;  //取v的邻接点编号vj

   if(visited[vj]==0)Q.EnQueue(vj);      //vj未访问,入队

    p=p->next;      //取下一个邻接边结点

  }

  }

 }

  使用邻接链表来表示图时,其BFS算法的时间复杂度为O(n+e),此处e为无向图中边的数目或有向图中弧的数目。

图的最小生成树

1、生成树的定义

   若从图的某顶点出发,可以系统地访问到图中所有顶点,则遍历时经过的边和图的所有顶点所构成的子图,称作该图的生成树。

(此定义不仅仅适用于无向图,对有向图同样适用。

(1)若G是强连通的有向图,则从其中任一顶点v出发,都可以访问遍G中的所有顶点,从而得到以v为根的生成树。

(2)若G是有根的有向图,设根为v,则从根v出发可以完成对G的遍历,得到G的以v为根的生成树。

【例】下图中(a)图是以v0为根的有向图,它的DFS生成树和BPS生成树分别如图(b)和(c)所示。

(3)若G是非连通的无向图,则要若干次从外部调用DFS(或BFS)算法,才能完成对G的遍历。

每一次外部调用,只能访问到G的一个连通分量的顶点集,这些顶点和遍历时所经过的边构成了该连通分量的一棵DFS(或BPS)生成树。

G的各个连通分量的DFS(或BFS)生成树组成了G的DFS(或BFS)生成森林。

(4)若G是非强连通的有向图,且源点又不是有向图的根,则遍历时一般也只能得到该有向图的生成森林。

【例】对图7.9中的图G,当按深度和广度优先搜索法进行遍历就可以得到图7.10所示的两种不同的生成树,并分别称为深度优先生成树和广度优先生成树。

2.最小生成树

如果连通图是一个网络,称该网络中所有生成树中权值总和最小的生成树为最小生成树(也称最小代价生成树)。

图7.12给出图7.11(a)的几棵生成树。

其中(a)的权为26,(b)的权为16,(c)的权为15,可以证明(c)为一棵最小生成树。

       

3、最小生成树性质(MST性质)

(1)MST性质

    最小生成树性质:

设G=(V,E)是一个连通网络,U是顶点集V的一个真子集。

若(u,v)是G中所有的一个端点在U(u∈U)里、另一个端点不在U(即v∈V-U)里的边中,具有最小权值的一条边,则一定存在G的一棵最小生成树包括此边(u,v)。

(2)MST性质的证明

    为方便说明,先作以下约定:

①将集合U中的顶点看作是红色顶点,②而V-U中的顶点看作是蓝色顶点,③连接红点和蓝点的边看作是紫色边,④权最小的紫边称为轻边(即权重最"轻"的边)。

于是,MST性质中所述的边(u,v)就可简称为轻边。

用反证法证明MST性质:

  假设G中任何一棵MST都不含轻边(u,v)。

则若T是G的一棵MST,则它不含此轻边。

   由于T是包含了G中所有顶点的连通图,所以T中必有一条从红点u到蓝点v的路径P,且P上必有一条紫边(u',v')连接红点集和蓝点集,否则u和v不连通。

当把轻边(u,v)加入树T时,该轻边和P必构成了一个回路。

删去紫边(u',v')后回路亦消除,由此可得另一生成树T'。

    T'和T的差别仅在于T'用轻边(u,v)取代了T中权重可能更大的紫边(u',v')。

因为w(u,v)≤w(u',v'),所以

   w(T')=w(T)+w(u,v)-w(u',v')≤w(T)

故T'亦是G的MST,它包含边(u,v),这与假设矛盾。

    所以,MST性质成立。

4、普里姆(Prim)算法

(1)算法思想

    T=(U,TE)是存放MST的集合。

  ①T的初值是({r},¢)

   即最小生成树初始时只有一个红点r,没有红边。

  ②T经过n-1次如下步骤操作,最后得到一棵含n个顶点,n-1条边的最小生成树

    ⒈选择紫边集中一条轻边并扩充进T

  ⒉将轻边连接的蓝点改红点

  ⒊将轻边改红边

    ⒋修改紫边集

(2)较小紫边集的构造

    若当前形成的T中有k个顶点,|U|=k,|V-u|=n-k,故可能的紫边数目是k(n-k)。

从如此大的紫边集中选择轻边是低效的。

因此,必须构造较小的紫边集。

    对于每个蓝点v∈V-U,从v到各红点的紫边中,只有最短的那一条才有可能是轻边。

因此,只须保留所有n-k个蓝点所关联的最短紫边作为轻边的候选集即可。

(3)候选紫边集合的修改

    当把轻边(u,v)扩充到T时,因为v由蓝变红,故对每个剩余的蓝点j,边(v,j)就由非紫边变为紫边,这条新紫边的长度可能小于蓝点j原来所关联的最短紫边的长度。

因此,用长度更小的新紫边取代那些原有的最短紫边。

(4)算法的执行过程

注意:

若候选轻边集中的轻边不止一条,可任选其中的一条扩充到T中。

连通网的最小生成树不一定是惟一的,但它们的权相等。

【例】在上图(e)中,若选取的轻边是(2,4)而不是(2,1)时,则得到如图(h)所示的另一棵MST。

 (5)算法特点

    该算法的特点是当前形成的集合T始终是一棵树。

将T中U和TE分别看作红点和红边集,V-U看作蓝点集。

算法的每一步均是在连接红、蓝点集的紫边中选择一条轻边扩充进T中。

MST性质保证了此边是安全的。

T从任意的根r开始,并逐渐生长直至U=V,即T包含了C中所有的顶点为止。

MST性质确保此时的T是G的一棵MST。

因为每次添加的边是使树中的权尽可能小,因此这是一种"贪心"的策略。

(7)算法分析

    该算法的时间复杂度为O(n2)。

与图中边数无关,该算法适合于稠密图。

5、克鲁斯卡尔(Kruskal)算法

(1)算法思想

  ①T的初始状态

      只有n个顶点而无边的森林T=(V,¢)

  ②按边长递增的顺序选择E中的n-1安全边(u,v)并加入T,生成MST

注意:

    安全边指两个端点分别是森林T里两棵树中的顶点的边。

加入安全边,可将森林中的两棵树连接成一棵更大的树

    因为每一次添加到T中的边均是当前权值最小的安全边,MST性质也能保证最终的T是一棵最小生成树。

(2)算法特点

    该算法的特点是:

当前形成的集合T除最后的结果外,始终是一个森林。

(3)Kruskal算法简单描述

T=(V,Φ);

While(T中所有边数

{从E中选取当前最短边(u,v);

从E中删去边(u,v);

If((u,v)并入T之后不产生回路)将(u,v)并入T中;

}

具体算法演示请点击查看【算法演示】

(4)用Kruskal算法构造最小生成树的过程

(5)算法分析

 该算法的时间复杂度为O(elge)。

Kruskal算法的时间主要取决于边数。

它较适合于稀疏图。

 

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

当前位置:首页 > 高等教育 > 法学

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2