第八章 图
This presentation is the property of its rightful owner.
Sponsored Links
1 / 128

图 图的存储表示 图的遍历 无向图的连通分量和生成树 最短路径 拓扑排序 PowerPoint PPT Presentation


  • 92 Views
  • Uploaded on
  • Presentation posted in: General

第八章 图. 图 图的存储表示 图的遍历 无向图的连通分量和生成树 最短路径 拓扑排序. 一、图. 图 应用最广泛的数据结构。 不同于树的另一种 非线性结构 每个顶点可以与 多个 其他顶点相关联,各顶 点之间的关系是 任意 的。 简单图 没有自身环,两点间至多一条边. v 1. v 3. v 2. v 1. v 5. v 3. v 4. v 4. v 2. 无向图. 有向图. 图的基本概念. G=<V, E>

Download Presentation

图 图的存储表示 图的遍历 无向图的连通分量和生成树 最短路径 拓扑排序

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


3858194

第八章 图

  • 图的存储表示

  • 图的遍历

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

  • 最短路径

  • 拓扑排序


3858194

一、图

图 应用最广泛的数据结构。

不同于树的另一种非线性结构

每个顶点可以与多个其他顶点相关联,各顶

点之间的关系是任意的。

简单图 没有自身环,两点间至多一条边

v1

v3

v2

v1

v5

v3

v4

v4

v2

无向图

有向图


3858194

图的基本概念

G=<V, E>

V={v1,v2,······,vn} 顶点集

E={ (vi, vj) | vi,vj∈V, vi≠vj} 边集 无向图

E={<vi, vj>|vi ,vj∈V}有向边集 有向图

有向边 <vi, vj> , vi起点弧尾, vj终点弧头

TD(vi):一个顶点的度,以vi为端点的边的数目。

OD(vi): 出度, 以vi为起点的边的数目。

ID(vi): 入度,以vi为终点的边的数目。

TD(vi)= OD(vi)+ ID(vi)

OD=ID, TD=2|E|, |E| =1/2*TD

TD OD ID 为整个图的总度,出度,入度数。


3858194

图的基本概念

路径 vi······vj, 以vi为起点vj为终点的顶点序列。

路径的长 路径上边的数目,

简单路径 顶点都不重复的路径,

回路 环 首尾相接的路径,

简单回路 除第一个和最后一个顶点以外都不重

复的路径,

vivj连通 有路径 vi······vj,

连通图 任意两点都连通,

有向图 vivj强连通 vivj连通 vjvi也连通,

强连通图 任意两点都强连通。


3858194

v2

v2

v1

v1

v4

v4

v3

v3

v5

v5

弱连通

强连通


3858194

强连通分量:彼此强连通的顶点的子集

B

A

ABC

D

EFG

H

I

D

C

F

E

H

G

I


3858194

完全图 任意两点间都有边相关联的图。

无向完全图 共有边 1/2(n*(n-1)) 条,

有向完全图 共有边 n(n-1) 条。

稀疏图 |E|<nlog n

稠密图 |E|<nlog n

带权边 具有边长的边

有权图 图的所有边都是带权边。

网络 有权图


3858194

子图

G=(V, E), G’=(V’, E’)

如果 V’ V, E’ E ,

就称 G’是G的子图。

连通分量 一个图的极大连通子图。

强连通分量 一个图的极大强连通子图。

连通图的生成树 含有所有顶点的极小 连通图

n个顶点尽可能少边的连通图有n-1条边。

非连通图的生成森林:所有k个连通分支的生成树组成生成森林,共有n-k条边。


3858194

有向树

有向图连通图恰有一个顶点的入度为0,

其余顶点的入度都是1。

有向图的生成森林:

有向图的一个子图,含有所有顶点,构成若干互不相交的有向树,叫做生成森林。


3858194

二、图的存储结构

1.邻接矩阵

用矩阵表示图的顶点之间的相邻关系。

A[i,j]=1 (vi,vj)∈E

=0 o.w.

0 1 1 0 0

1 0 0 1 1

1 0 0 0 1

0 1 0 0 0

0 1 1 0 0

v1

v2

v5

v3

v4


3858194

无向图的邻接矩阵是对称矩阵

0 1 1 0 0

1 0 0 1 1

1 0 0 0 1

0 1 0 0 0

0 1 1 0 0

TD(vi)=ΣA[i,j] i行数字的和等于vi的度

v1

v2

v5

v3

v4

n

j=1

n

n

|E|=1/2 ΣΣA[i,j] 全部数字的和等于边数*2

i=1

j=1


3858194

有向图的邻接矩阵

A[i,j]=1 <vi,vj>∈E

=0 o.w.

0 1 1 0

0 0 0 1

1 0 0 0

1 0 0 0

v3

v1

v4

v2

n

OD(vi)=ΣA[i,j] i行数字的和等于vi的出度

j=1

n

n

|E|= ΣΣA[i,j] 全部数字的和等于边数

i=1

j=1


3858194

网的邻接矩阵

A[i,j]=wi (vi,vj)∈E 权为wi

=∞ o.w.

∞ 5 ∞ 7 ∞ ∞

∞ ∞ 4 ∞ ∞ ∞

8 ∞ ∞ ∞ ∞ 9

∞ ∞ 5 ∞ ∞ 6

∞ ∞ ∞ 5 ∞ ∞

3 ∞ ∞ ∞ 1 ∞

5

v2

v1

4

8

v3

7

3

9

v6

6

1

5

v5

v4

5


3858194

#ifndef GRAPH_CLASS#define GRAPH_CLASS#include <iostream.h>#include <fstream.h>#include "stack.h"#include "pqueue.h"#include "queue.h"#include "seqlist2.h"const intMaxGraphSize= 25;


3858194

template <class T>class VertexIterator;template <class T> class Graph{ SeqList<T> vertexList; int edge [MaxGraphSize][MaxGraphSize]; int graphsize; int FindVertex(SeqList<T> &L, const T& vertex); int GetVertexPos(const T& vertex);


3858194

public: Graph(void); int GraphEmpty(void) const; int GraphFull(void) const; int NumberOfVertices(void) const;int GetWeight(const T& vertex1, const T& vertex2); SeqList<T>& GetNeighbors(const T& vertex); int GetFirstNeighbor(const int v); int GetNextNeighbor(const int v1, const int v2);


3858194

// graph modification methods void InsertVertex(const T& vertex);void InsertEdge(const T& vertex1, const T& vertex2, int weight); void DeleteVertex(const T& vertex);void DeleteEdge(const T& vertex1, const T& vertex2);


3858194

// utility methods void ReadGraph(char *filename);SeqList<T>& DFS( ); SeqList<T>& DFS(const int v, int *visited); SeqList<T>& DepthFirstSearch(const T& beginVertex); SeqList<T>& BreadthFirstSearch(const T& beginVertex);int MinimumPath(const T& sVertex, const T& eVertex); // iterator used to scan the vertices friend class VertexIterator<T>;};


3858194

2. 邻接表

A

B

E

C

D

有向图可以有正反两种邻接表


3858194

邻接表表示的图的定义

Const int MaxGraphSize=25;

template <class T>

struct Edge //边的类 第一个顶点是隐式的

{ int adjvex; //第二个顶点的编号

int weight;

Edge<T> *next; //指向下一条边的指针

Edge( ):adjvex(0),weight(0),next(0){ }

Edge(int v,int w):ajvex(v),weight(w),next(0){ }

~Edge( ){delete next;}

};


3858194

template<class T>

struct VNode

{ Tvertex;

Edge<T> *firstedge;

}

template<class T>

class ALGraph

{ VNode vertexArry[MaxGraphSize] ;

int vexNum, edgeNum;

int FindVertex(SeqList<T> &L, const T& vertex); int GetVertexPos(const T& vertex);


3858194

public: ALGraph(void); int GraphEmpty(void) const; int GraphFull(void) const; int NumberOfVertices(void) const;int GetWeight(const T& vertex1, const T& vertex2); SeqList<T>& GetNeighbors(const T& vertex); int GetFirstNeighbor(const int v); int GetNextNeighbor(const int v1, const int v2);


3858194

// graph modification methods void InsertVertex(const T& vertex);void InsertEdge(const T& vertex1, const T& vertex2, int weight); void DeleteVertex(const T& vertex);void DeleteEdge(const T& vertex1, const T& vertex2);


3858194

// utility methods void ReadGraph(char *filename);SeqList<T>& DFS( ); SeqList<T>& DFS(const int v, int *visited); SeqList<T>& DepthFirstSearch(const T& beginVertex); SeqList<T>& BreadthFirstSearch(const T& beginVertex);int MinimumPath(const T& sVertex, const T& eVertex);};


3858194

//邻接矩阵表示的图的实现// constructor initialize entries in the adjacency matrix// to 0 and sets the graphsize to 0template <class T>Graph<T>::Graph(void){ for (int i = 0; i < MaxGraphSize; i++) for (int j = 0; j < MaxGraphSize; j++)edge[i][j] = 0;graphsize = 0;}


3858194

图的输入格式

0 1 1 0 0

1 0 0 1 1

1 0 0 0 1

0 1 0 0 0

0 1 1 0 0

A

B

顶点数

顶点序列

边数

边序列

E

C

D

5

A B C D E

5

A B 1

A C 1

B D 1

B E 1

C E 1

Graph<char>G;

G.ReadGraph(“graph.dat”);


3858194

template <class T >void Graph< T >::ReadGraph(char *filename){ int i, nvertices, nedges;T S1, S2; int weight; ifstream f;f.open(filename, ios::in | ios::nocreate); if(!f){ cerr << "Cannot open " << filename << endl; exit(1); }f >> nvertices; for (i = 0; i < nvertices; i++) { f >> S1; InsertVertex(S1); }f >> nedges; for (i = 0; i < nedges; i++) { f >> S1; f >> S2; f >> weight; InsertEdge(S1,S2, weight); }f.close( ); }


Template class t int graph t numberofvertices void const return graphsize

template <class T>int Graph<T>::NumberOfVertices(void) const{ return graphsize;}

template <class T>int Graph<T>::GraphEmpty(void) const{ return graphsize == 0;}


3858194

template <class T>int Graph<T>::GetVertexPos(const T& vertex){ SeqListIterator<T> liter(vertexList); int pos = 0;while(!liter.EndOfList( ) && liter.Data( ) != vertex) { pos++; liter.Next( ); } if (liter.EndOfList( )) { cerr << "GetVertex: the vertex is not in the graph." << endl; pos = -1; } return pos;}


3858194

template <class T>int Graph<T>::GetWeight(const T& vertex1, const T& vertex2){ int pos1=GetVertexPos(vertex1), pos2=GetVertexPos(vertex2); if (pos1 == -1 || pos2 == -1) { cerr << "GetWeight: a vertex is not in the graph." << endl; return -1; } return edge[pos1][pos2];}


3858194

template <class T>SeqList<T>& Graph<T>::GetNeighbors(const T& vertex){ SeqList<T> *L; SeqListIterator<T> viter(vertexList);L = new SeqList<T>; int pos = GetVertexPos(vertex); if (pos == -1) { cerr << "GetNeighbors: the vertex is not in the graph." << endl; return *L; } for (int i = 0; i < graphsize; i++) { if (edge[pos][i] > 0) L->Insert(viter.Data( ));viter.Next( ); } return *L;}


3858194

template <class T>int Graph<T>::GetFirstNeighbor(const int v){if (v <0||v>graphsize) { cerr << “The vertex is not in the graph." << endl; return -1; } for(int i = 0; i < graphsize;i++) if(edge[v][i] >0) return i; return -1;}


3858194

template <class T>int Graph<T>::GetNextNeighbor(const int v, const int v1){if (v <0||v>graphsize ||v1 <0||v1>graphsize) { cerr << “The vertex is not in the graph." << endl; return -1; } for(int i = v1+1; i < graphsize;i++)if(edge[v][i] >0) return i; return -1;}


3858194

template <class T>void Graph<T>::InsertVertex(const T& vertex){ if (graphsize+1 > MaxGraphSize) { cerr << "Graph is full" << endl; exit (1); }vertexList.Insert(vertex);graphsize++;}


Vertex1 vertex2

插入一条边 <vertex1,vertex2>

检查顶点vertex1,vertex2是否在图的

顶点表中,有一个不在图中就给出

错误信息返回。

在图中,则确定位置pos1,pos2,

设置边(pos1,pos2)的权值。


3858194

template <class T>void Graph<T>::InsertEdge(const T& vertex1, const T& vertex2, int weight){ int pos1=GetVertexPos(vertex1), pos2=GetVertexPos(vertex2); if (pos1 == -1 || pos2 == -1) { cerr << "InsertEdge: a vertex is not in the graph." << endl; return; } edge[pos1][pos2] = weight;}


3858194

删除一个顶点

如果顶点不在表中给出错误信息返回。

如果在表中,确定位置pos

把邻接矩阵分成四块

pos

第I块中边不动

第II块中边列左移

第III块中边行上移

第IV块中边

列左移行上移

I

II

pos

III

IV


3858194

template <class T>void Graph<T>::DeleteVertex(const T& vertex){ int pos = GetVertexPos(vertex); int row, col; if (pos == -1) { cerr << "DeleteVertex: a vertex is not in the graph." << endl; return; }vertexList.Delete(vertex); graphsize--; for (row = 0; row < pos; row++) for (col = pos + 1;col < graphsize;col++)edge[row][col-1] = edge[row][col]; for (row = pos + 1;row < graphsize;row++) for (col = pos + 1;col < graphsize;col++)edge[row-1][col-1] = edge[row][col]; for (row = pos + 1;row < graphsize;row++) for (col = 0; col < pos; col++)edge[row-1][col] = edge[row][col]; }


3858194

template <class T>void Graph<T>::DeleteEdge(const T& vertex1, const T& vertex2){ int pos1=GetVertexPos(vertex1), pos2=GetVertexPos(vertex2); if (pos1 == -1 || pos2 == -1) { cerr << "DeleteEdge: a vertex is not in the graph." << endl; return; }edge[pos1][pos2] = 0;}


3858194

template <class T>int Graph<T>::FindVertex(SeqList<T> &L, const T& vertex){ SeqListIterator<T> iter(L); int ret = 0; while(!iter.EndOfList( )) { if (iter.Data( ) == vertex) { ret = 1; break; }iter.Next( ); } return ret;}


Template class t class vertexiterator public seqlistiterator t public vertexiterator graph t g

template <class T >class VertexIterator: public SeqListIterator< T >{ public:VertexIterator(Graph< T >& G);};

template <class T >VertexIterator<T>::VertexIterator(Graph<T>& G): SeqListIterator< T > (G.vertexList){}


3858194

深度优先搜索用递归算法

三、图的遍历

输出顶点并标记

循环:{

递归计算第一个邻接点

(如 未标记)

下一个邻接点

}

ABCDEIHF

A

F

B

C

E

H

D

I


3858194

template <class T>

SeqList<T>& Graph<T>::DFS( )

{ int *visited=new int[graphsize];

for(int i=0;i<graphsize;i++)

visited[i]=0;

SeqList<T> *L=new SeqList<T>;

*L=DFS(0,visited);

delete[]visited;

return *L;

}


3858194

template <class T>

SeqList<T>& Graph<T>::DFS(const int v, int *visited)

{ SeqList<T>*L;

Tvertex=vertexList.GetData(v);

L=new SeqList<T>;

visited[v]=1;

L->Insert(vertex);

int w=GetFirstNeighbor(v)

while(w!=-1)

{if(!visited[w])DFS(w,visited);

w=GetNextNeihbor(v,w);

}

return *L;

}


3858194

//深度优先搜索2 不用递归用栈

L

F

B

A

S

SeqList<T> L; //输出顶点

Stack<T> S; //存储待算顶点

HE

B

AF

IE

B

A

AFH

F

B

D

B

AFHIE

C

E

H

C

AFHIEDB

D

I

AFHIEDBC


3858194

//深度优先搜索2 不用递归用栈

template <class T>SeqList<T> & Graph<T>::DepthFirstSearch(const T& beginVertex){ Stack<T> S; SeqList<T> *L, adjL; SeqListIterator<T> iteradjL(adjL); Tvertex; L = new SeqList<T>; S.Push(beginVertex);


3858194

while (!S.StackEmpty( )) {vertex = S.Pop( ); if (!FindVertex(*L,vertex)) { (*L).Insert(vertex); adjL = GetNeighbors(vertex);iteradjL.SetList(adjL);for(iteradjL.Reset( );!iteradjL.EndOfList( );iteradjL.Next( )) if (!FindVertex(*L,iteradjL.Data( ))) S.Push(iteradjL.Data( )); } } return *L; // return list}


3858194

广度优先搜索

用队列

ABFCHEDI

Queue<T> Q;

SeqList<T> L,adjL;

L

Q

BF

A

AB

FCH

A

ABF

CHE

F

B

HED

ABFC

C

E

H

EDI

ABFCH

D

I

ABFCHEDI


3858194

template <class T>SeqList< T >& Graph< T >::BreadthFirstSearch( const T & beginVertex){ Queue< T > Q; SeqList< T > *L, adjL; SeqListIterator< T > iteradjL(adjL);Tvertex; L = new SeqList< T >; Q.QInsert(beginVertex); // initialize the queue while (!Q.QEmpty( )) { vertex = Q.QDelete( ); if (!FindVertex(*L,vertex)) { (*L).Insert(vertex); adjL = GetNeighbors(vertex);iteradjL.SetList(adjL); for(iteradjL.Reset( );!iteradjL.EndOfList( );iteradjL.Next( )) { if (!FindVertex(*L,iteradjL.Data( )))Q.QInsert(iteradjL.Data( )); } } } return *L; }


3858194

四、无向图的连通分量和生成树

一个图中互相连通的点的极大子集叫连通分量。

从一点出发,深度优先或广度优先搜索到的子图就是连通分量。

从连通图的任意一点出发,深度优先搜索到的子图也就是图的生成树,也叫深度优先生成树;广度优先搜索到的生成树,也叫广度优先生成树;

非连通图遍历得到的是生成森林:

从一点出发深度优先搜索并标记,得到一棵树,再继续得到另一棵树,直到取遍所有顶点。


3858194

template <class T>void PrintList(SeqList<T> &L){SeqListIterator<T> liter(L);for (liter.Reset( ); !liter.EndOfList( ); liter.Next( )) cout << liter.Data( ) << " ";}


3858194

template <class T>int PathConnect (Graph<T> &G, Tv, Tw){ SeqList<T> L;// find vertices connected to vL = G.DepthFirstSearch(v);// is w is in the list, return TRUE if (L.Find(w)) return 1; else return 0;}


3858194

template <class T>void UConnectedComponent (Graph<T> &G){ VertexIterator<T> viter(G); SeqList<T> markedList, L; for (viter.Reset( ); !viter.EndOfList( ); viter.Next( )) { if (!markedList.Find(viter.Data( ))) { L.ClearList( ); L = G.DepthFirstSearch(viter.Data( )); SeqListIterator<T> liter(L); for(liter.Reset( );!liter.EndOfList( );liter.Next( ))markedList.Insert(liter.Data( ));PrintList(L); cout << endl; } } }


3858194

强连通分量:彼此强连通的顶点的子集

B

ABC

D

EFG

H

I

做法:从一点v0出发作深度优先搜索,得到一个顶点序列L。检查L上的点到v0,是否也连通,所有连通的点组成一个强连通分量。

重复着一过程,直到取遍所有顶点。

A

D

C

F

E

H

G

I


3858194

template <class T>void ConnectedComponent (Graph<T> &G){ VertexIterator<T> viter(G); SeqList<T> markedList, scList, L; for (viter.Reset( ); !viter.EndOfList( ); viter.Next( )) { if (!markedList.Find(viter.Data( ))) { scList.ClearList( );L = G.DepthFirstSearch(viter.Data( )); SeqListIterator<T> liter(L);


3858194

for (liter.Reset( );!liter.EndOfList( );liter.Next( )) if (PathConnect(G,liter.Data( ),viter.Data( ))) { scList.Insert(liter.Data( ));markedList.Insert(liter.Data( )); }PrintList(scList); cout << endl; } }}


Minimum cost spanning tree

最小生成树 Minimum-cost Spanning Tree

带权连通图(网络)中权值总和最小的生成树叫最小生成树

连接所有n个点的通讯网络的最短线路。


3858194

Prim 普里姆算法

设 G=<V,E>,

1. 令 U={v0}, T={ }.

2. 对任意u∈U, v∈V-U, (u,v)∈E,

找到权最小的边(u1,v1),

令U=U∪{v1}, T=T∪{(u1,v1)}

3. 重复2,直至U=V.

得到 T就是最小生成树。

T中共有n-1条边


3858194

A

28

F

16

10

14

B

D

H

24

18

25

12

C

E

22

A

28

F

16

10

14

B

D

H

24

18

25

12

C

E

22


3858194

A

A

5

5

6

6

1

1

B

D

B

D

5

5

5

5

C

C

4

4

6

2

6

2

3

3

E

F

E

F

6

6

U={A}, T={(A,C)}

U={A,C}, T={(A,C),(C,F)}

U={A,C,F}, T={(A,C),(C,F),(D,F)}

U={A,C,F,D}, T={(A,C),(C,F),(D,F),(B,C)}

U={A,C,F,D,B}, T={(A,C),(C,F),(D,F),(B,C),(B,E)}

U={A,C,F,D,B,E}


3858194

A

定义数组 closeEdge[n]

纪录每点到U的最短距离 (点,距离)

U中点距离为0,

每加入一个新点, 数组更新一次

5

6

1

B

D

5

5

C

4

6

2

3

E

F

6


3858194

template<class T>struct MiniCostEdgeInfo { Tadjvex; int lowcost; };template <class T>int operator<(MiniCostEdgeInfo<T> a, MiniCostEdgeInfo<T> b){ return a.lowcost<b.lowcost;}


3858194

template <class T>int minimum(MiniCostEdgeInfo<T> *a,int n){ for(int i=0;i<n;i++) if(a[i].lowcost!=0) break; int min=i; for(i=min+1;i<n;i++) if(a[i].lowcost!=0&&a[i]<a[min]) min=i ; return min;}


3858194

template<class T>T GetVertex(Graph<T> G,int pos){ int i, n=G.NumberOfVertices( ); if(pos<0||pos>=n) {cerr<<"There are not so many vertices!"; return 0; } VertexIterator<T> liter(G);i = 0; while(!liter.EndOfList( ) && i != pos) { i++;liter.Next( ); } return liter.Data( );}


3858194

template<class T >void MiniSpanTreePrim(Graph< T > G){ int j,k,l,n=G.NumberOfVertices( ); MiniCostEdgeInfo< T > * closeEdge;closeEdge =new MiniCostEdgeInfo< T >[n];Ts,w, v=GetVertex(G,0); closeEdge[0].lowcost=0;//起始点v0加进U for(int i=1;i<n;i++)//初始化closeEdge数组{ w=GetVertex(G,i); l=G.GetWeight(v,w);closeEdge[i].adjvex=v; if(l>0)closeEdge[i].lowcost=l; else closeEdge[i].lowcost=maxint; }


3858194

for( i=1;i<n;i++) //双重循环复杂度O(n2)与边数无关{ k=minimum(closeEdge,n);//确定closeEdge中最小值v=closeEdge[k].adjvex;//取出这一边w=GetVertex(G,k); l=closeEdge[k].lowcost; cout<<“\n”<<v<<“ ”<<w<<“ ”<<l<<endl;closeEdge[k].lowcost=0; //将w加进U for( j=0;j<n;j++) //更新closeEdge { v=GetVertex(G,j); l=G.GetWeight(w,v); if(l>0&&l<closeEdge[j].lowcost) { closeEdge[j].lowcost=l;closeEdge[j].adjvex=w; } } } }


Void main graph char g g readgraph sctest dat connectedcomponent g minispantreeprim g

void main( ){ Graph<char> G;G.ReadGraph("sctest.dat");ConnectedComponent(G);MiniSpanTreePrim(G);}


Kruskal

Kruskal 克鲁斯卡尔算法

G=(V,E) 连通图

令 T=(V,{ }) 是G的所有顶点而无边的非连通图。

  • 选择E中权值最小的边,

    若该边连接T的两个连通分量,将它加入T,

    这时T的连通分量减少1;

    否则选下一条权值最小的边。

  • 重复1 n-1次直到T连通。

    T 就是最小生成树


3858194

A

A

5

5

6

6

1

1

1

B

D

5

5

B

D

5

5

5

C

C

4

6

2

4

4

3

6

2

2

3

3

E

F

E

F

6

6

T={ (A,C), (D,F), (B,E) }

T={ (A,C), (D,F), (B,E), (C,F) }

T={ (A,C), (D,F), (B,E)

(C,F), (B,C) }

A

B

D

C

E

F


Kruskal1

Kruskal 克鲁斯卡尔算法

把所有边都放进优先队列。

重复以下步骤,直至T连通:

取出最小边,判断这条边的两个端点,如果属于T的不同的连通分支,加入T, 把两分支联成一个。否则放弃。

问题:

1.如何判断两端点是否在同一 分支?

2.如何把两个分支连到一起?

A

B

D

C

E

F

并查集MFS方法


3858194

问题:1.如何判断两点是否在同一 分支?

用双亲表示法表示结点,每个结点都有一个数据和一个指向双亲的地址的指针。根结点的双亲为-1。

同一分支的结点联成一棵树。从每个结点都可以向上找到这个分支的根。两个结点各自所在分支的根相同,则他们处在同一分支,根不同,则所在分支不同。

D

B

A

C

F

G

E

H


3858194

问题2.如何把两个分支连到一起?

将第二个分支与第三个分支连起来,只要让结点D的双亲由-1改为指向 A。

B

A

D

B

A

D

C

E

C

F

G

E

F

G

H

H


3858194

双亲表示法

用数组存储树的结点

每个结点中附设一个字段

指示其父结点的位置

D

A

B

C

E

F

G

H


3858194

要将AE,CG相连

只要将E的双亲改为0,

G的双亲改为2

D

A

B

C

E

F

G

H


3858194

现在要加入边EG使连通度减小:

先查E所在分支的根,E的双亲是0,即A点,A的双亲是-1,A是根。

同样查到G所在分支的根是2,C点。

0≠2,根不同,分支不同。

将C连到A,即C点的双亲改为0,就可以。

A

B

C

D

E

F

G

H


3858194

双亲表示法结点定义

#define MAX_TREE_SIZE 100

template <class T>

struct PNode

{ T data;

int Parent;

}


3858194

树的双亲表示法定义

template <class T>

class PTree

{ PNode<T> nodes[MAX_TREE_SIZE];

int n; //number of nodes

public:

PTree( int m=0);

PNode<T> operator[ ](int i);

int PTreeInsert(T item, int pr);

T PTreeDelete(int i);

int PTreeSize( );

}


3858194

#include"ptree.h"template<class T>class MFSet:public PTree< T >{ public:MFSet( ){ } int Find(Titem); int FindRoot(int i); int FindRoot(Titem); void Merge(int root1,int root2);};

并查集类的定义


3858194

template <class T >int MFSet< T >::Find(Titem){ for(int i=0;i<n;i++) if(nodes[i].data==item) { return i; break;} return -1;}template <class T >int MFSet< T >::FindRoot(int i) { if(i<0||i>=n)return -1; for(int j=i;nodes[j].parent>=0;j=nodes[j].parent);return j; }


3858194

template <class T >int MFSet< T >::FindRoot(Titem){ int i=Find(item); return FindRoot(i);}template <class T >void MFSet< T >::Merge(int root1, int root2){ if(root1<0||root1>=n||root2<1||root2>=n) { cerr<<"Beyound the scope!"; } nodes[root2].parent=root1;}


3858194

#include<graph.h>#include<conncomp.h>#define maxint 32767template <class T>struct EdgeInfo{ TbeginVex, endVex; int cost;};


3858194

//Kruskal算法#define MaxInt 32767#include"APQueue.h"#include"Graph.h"#include"edgeinfo.h"#include"mfset.h"#include"PTree.h"typedefEdgeInfo<char> EI;


3858194

template <class T>void MiniSpanTreeKruskal(Graph< T > G){ int i, j, l, n=G.NumberOfVertices( );Titem,u,v; EI edge; MFSet< T > MFS; PQueue<EI> L; for(i=0;i<n;i++) { item=GetVertex(G,i); MFS.PTreeInsert(item,-1); }


3858194

for(i=0;i<n;i++) { u=GetVertex(G,i); for(j=0;j<n;j++) { v=GetVertex(G,j);l=G.GetWeight(u,v); if(l!=MaxInt&&l>0) { edge.beginVex=u;edge.endVex=v;edge.cost=l; L.PQInsert(edge); }} }


3858194

int count=1; while(count<n) { edge=L.PQDelete( );i=MFS.FindRoot(edge.beginVex); j=MFS.FindRoot(edge.endVex); if(i!=j){ cout<<edge.beginVex<<" "<<edge.endVex <<" "<<edge.cost<<endl;MFS.Merge(i,j); count++; } }}


Void main graph char g g readgraph sctest dat cout endl cout endl cout endl minispantreekruskal g

void main( ){ Graph<char> G;G.ReadGraph("sctest.dat"); cout<<endl; cout<<endl; cout<<endl;MiniSpanTreeKruskal(G);}


3858194

图上两点间最短距离

4

4

E

A

2

B

4

6

12

6

6

10

8

12

F

D

C

20

14


3858194

五、最短路径

两点间边数最少的路径

可用作交通自动咨询系统

两点间边权重的和最小的路径

用来计算两城市间路程最短,

时间最快,费用最省的路径


3858194

两点A,B之间边数最少的路径

从A点出发,对图做广度优先遍历。

从根A到B的路径就是边数最少的路径,也就是中转次数最少的路径。


3858194

单源点到其余各点权重和最小的路径

从v0到其余各点的最短路径

v5

60

100

30

v4

v0

(v0,v2) 10

10

20

(v0,v4,v3) 50

10

(v0,v4) 30

v3

v1

(v0,v4,v3,v5) 60

5

50

v2


Dijkstra

迪克斯特拉Dijkstra算法

按路径长度递增逐步产生最短路径

设集合S存放已经求出的最短路径的终点,开始,S中只有一个源点v0,以后每求得的一条最短路径就将终点加入S,直到全部顶点都加入到S.

定义一个数组 D[n]; n是图的顶点数。

D[i]=从源点v0到顶点vi最短路经的长度。

第一步 取D[i]为v0到vi的边的权值,无边时取值∞,

取一个最小值 D[j1]=min{D[i], i<n}

D[j1]是v0到vj1的最短路径的长度。


3858194

第一步

L={v0}

v5

60

j1=2

D[2]=10

是v0到v2的最短路径的长度

100

30

v4

v0

10

20

10

v3

v1

5

50

v2

L={v0,v2}


Dijkstra1

迪克斯特拉Dijkstra算法

已经有L={v0,v2} ,下一条最短路径(终点vj2),或者是(v0 vj2), 或者是(v0, vj1,vj2) 。

对每个顶点vi, 比较D[i]与D[j1]+arc[j1][i], 取其小

更新 D[i]=min{D[i], D[j1]+arc[j1][i]}

取 D[j2]=min{D[i], i<n,i≠j1 }

则 D[j2]是v0到vj2的最短路径的长度。


3858194

L={v0,v2}

第二步

v5

60

100

30

j2=4

D[4]=30

是v0到v4的最短路径的长度

v4

v0

10

20

10

v3

v1

5

50

v2

L={v0,v2,v4}


3858194

递归过程:重复第二步

设已经有v0到vj1 ,vj2···,vjk的最短路径

对每个顶点vi, vi ≠ vj1 ,vj2···,vjk,

更新

D[i]=min{D[i], D[jk]+arc[jk][i]}

D[jk+1]=min{D[i], i<n,i≠ j1 ,j2···,jk }

D[jk+1]是v0到vjk+1的最短路径的长.


Dijkstra2

迪克斯特拉Dijkstra算法

v5

60

100

L={v0,v2,v4,v3,v5}

30

v4

v0

10

20

10

时间复杂性O(n2)

v3

v1

5

50

v2


3858194

令L={vj1 ,vj2···,vjk-1}是已经求得的从v0出发的最短路径的终点的集合,可以证明下一条最短路径(终点vjk),是只通过S中顶点到达vjk的 。

否则设v0到vjk的路径中有一个不在S中出现的顶点vp,但是路径v0···vp···vjk比v0···vp长

应当先有v0···vp的最短路径,以归纳假设vp应当已经出现于L中。


Template class t struct pathinfo t startv endv int cost

template <class T > struct PathInfo{TstartV, endV; int cost;};

template <class T >int operator <= (const PathInfo< T >& a,

const PathInfo< T >& b){ return a.cost <= b.cost;}


3858194

//用优先序列实现最短路径算法template <class T >int Graph< T >::MinimumPath(const T & sVertex, const T & eVertex){ PQueue< PathInfo< T > > PQ(MaxGraphSize); PathInfo< T > pathData; SeqList< T > L, adjL; SeqListIterator< T > adjLiter(adjL);Tsv, ev; int mincost;


3858194

pathData.startV = sVertex; pathData.endV = sVertex;pathData.cost = 0; PQ.PQInsert(pathData); while (!PQ.PQEmpty( )) { pathData = PQ.PQDelete( ); ev = pathData.endV;mincost = pathData.cost; if (ev == eVertex) break; if (!FindVertex(L,ev)) {L.Insert(ev); sv = ev;adjL = GetNeighbors(sv);adjLiter.SetList(adjL);


3858194

for(adjLiter.Reset( );!adjLiter.EndOfList( ); adjLiter.Next( )) { ev = adjLiter.Data( ); if (!FindVertex(L,ev)) { pathData.startV = sv; pathData.endV = ev;pathData.cost = mincost+GetWeight(sv,ev); PQ.PQInsert(pathData); } } } }if (ev == eVertex) return mincost; else return -1;}


3858194

template<class T>T GetVertex(Graph<T> G,int pos){ int i, n=G.NumberOfVertices( ); if(pos<0||pos>=n) {cerr<<"There are not so many vertices!"; return 0; } VertexIterator<T> liter(G);i = 0; while(!liter.EndOfList( ) && i != pos) { i++;liter.Next( ); } return liter.Data( );}


3858194

template<class T>void ShortestPathDijkstra(Graph<T> G, int v0,int *D,int**P){ int i, j,k,l,min, n=G.NumberOfVertices( );Tu,v,w;u=GetVertex(G,v0); int *final=new int[n]; for( i=0;i<n;i++) { final[i]=0;v=GetVertex(G,i); for(j=0;j<n;j++)P[i][j]=0;//initial P[i][j]D[i]=G.GetWeight(u,v); //initial D[i] if(D[i]<MaxInt){ P[i][v0]=1;P[i][i]=1;}// p[i][j]=1 iff vertex j is in the path from v0 to i }


3858194

D[v0]=0; final[v0]=1; for(i=1;i<n;i++) { min=MaxInt; for(j=1;j<n;j++) //Get the minimum D[k] if(final[j]==0) //vertex j has not marked. if(D[j]<min) { k=j; min=D[j];}final[k]=1; //marked vertex k, v=GetVertex(G,k); //found the shortest path for(j=1;j<n;j++) { w=GetVertex(G,j); l=G.GetWeight(v,w)+min;if(!final[j]&&(l<D[w])) { D[w]=l; //renew D[w]P[j]=P[k]; P[j][j]=1; } } } }


3858194

void main( ){ Graph<char> G;G.ReadGraph("sctest.dat"); int n=G.NumberOfVertices( ); int *D=new int[n]; int **P=new (int**[n])[n];ShortestPathDijkstra(G,0,D,P); for(int i=0;i<n;i++) { cout<<"P["<<i<<"]={ "; cout<<P[i][0]; for(int j=1;j<n;j++) cout<<","<<P[i][j]; cout<<"}"<<endl; }}


3858194

每一对顶点之间的最短路径

可让每个顶点作起始点以用Dijkstra算法算一遍,共n遍,时间复杂性O(n3).

弗洛伊德Floyd算法更直接


Floyd

弗洛伊德Floyd算法

定义Dk(u,v)为从u到v的长度最短的k-path.

假设已知从u到v的最短(k-1)-path,则最短k-path要么经过,要么不经过顶点k。如果经过顶点k,则最短k-path是从u到k的最短(k-1)-path,再连接从k到v的最短(k-1)-path。如果不经过顶点k,则最短路径保持k-1-path不变。


3858194

6

v0

v1

4

11

2

3

v2


Floyd1

弗洛伊德Floyd算法

int **D=new (int**[n])[n];

int ***P= new ((int***[n])[n])[n];

D-1[i][j]=arc[i][j];

Dk[i][j]=min{Dk-1[i][j], Dk-1[i][k]+ Dk-1[k][j]}


Include graph h define maxint 32767 typedef int distancematrix typedef int pathmatrix

#include"graph.h"#define MaxInt 32767typedef int** DistanceMatrix;typedef int** PathMatrix;


3858194

template <class T>void ShortestPathFloyd( Graph<T> G,PathMatrix *&P, DistanceMatrix& D){ int n=G.NumberOfVertices( ); int i,j,k,l,t; Tu,v,w; for(i=0;i<n;i++) { u=GetVertex(G,i); for(j=0;j<n;j++) { v=GetVertex(G,j);D[i][j]=MaxInt;l=G.GetWeight(u,v); if(l>0)D[i][j]=l; for(k=0;k<n;k++) P[i][j][k]=0; if(D[i][j]<MaxInt) {P[i][j][i]=1;P[i][j][j]=1;} }


3858194

for(k=0;k<n;k++)for(i=0;i<n;i++) for(j=0;j<n;j++) if(D[i][k]+D[k][j]<D[i][j]) { D[i][j]=D[i][k]+D[k][j]; for(t=0;t<n;t++)P[i][j][t]=P[i][k][t]||P[k][j][t]; } }}


3858194

void main( ) { Graph<char> G;G.ReadGraph("sctest.dat"); int n=G.NumberOfVertices( ); DistanceMatrix D=new (int** [n])[n]; PathMatrix* P=new (int***)[n]; for(int i=0;i<n;i++)P[i]=new (int** [n])[n];ShortestPathFloyd(G,P,D); for( i=0;i<n;i++) {cout<<endl; for(int j=0;j<n;j++) cout<<D[i][j]<< " "; } }


3858194

六、拓扑排序


Directed acycline graph

有向无环图 directed acycline graph

有向无环图可以用来描述一项工程的流程

也叫施工流程图,生产流程图,学习流程图等等。

AOV网Activity on Vertex Network

顶点表示一项工作,有向边表示前一项工作完成后才能开始后一项工作。

工作顺序:

ABCDEF

ACDEBF

B

A

F

C

必须无环

构成偏序

D

E


3858194

拓扑排序

为一个AOV网建立一个全序序列,

网中原有先后次序保持不变。

算法原理:

1. 选择一个没有前驱的顶点输出,

2. 去掉这个顶点以及从这点出发的所有边。

重夫1.2.直到所有顶点都输出完毕

同样可以计算拓扑逆序,即由末尾向前递归


3858194

拓扑排序算法

  • 用一个数组纪录图G的每个顶点的入度

  • 找出第一个入度为0的点输出

  • 找出与之相连的所有顶点,将他们的入度都减1。

  • 重复2.3.直至没有入度为0的点

用出度可以计算拓扑逆序


3858194

//入度算法函数

int n=G.NumberOfVertices( );

int indegree[n];

void InDegree(Graph<T> G,*indegree)

{ int i,j=0; T u,v;

for( i =0; i <n; i ++)indegree[i]=0;

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

{u=GetVertex(G, i);

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

v=GetVertex(G, j);

if(G.GetWeight(u,v))indegree[j]++;}

}


Template class t void topologicalsort graph t g

Template<class T>void TopologicalSort(Graph<T> G)

{ int n=G.NumberOfVertices;

int *indegree=new int[n];

Stack<int> S;

InDegree(G,indegree);//纪录顶点的入度

for(int i=0;i<n;i++) if(indegree[i]==0)S.Push(i);

int count=0; //入度为0的点进栈

while(!S.StackEmpty( ))

{ i=S.Pop( ); //取出一个入度0的点输出

cout<<GetVertex(G,i)<<“ ”; ++count;

for(int j=G.GetFirstNeighber(i);j>0;

j= G.GetNextNeighber(i,j))

if(--indegree[j]==0)S.Push(j); //邻接点入度减1}

if(count<n)cout<<“There is a circle!”;

}


3858194

AOE网 工程进度表示

AOE网 Active on Edges

顶点表示事件,边表示活动以及所需时间

v2

v7

a1=6

a4=1

a7=9

a10=2

v5

v1

v9

a5=1

a2=4

a8=7

a11=4

v8

v3

a3=5

a9=4

a6=2

v4

v6


Aoe critical path

AOE网的关键路径critical path

从源点v1到汇点v9的最长路径叫关键路径

(起点) (终点)

入度0唯一 出度0唯一

v2

v7

a1=6

a4=1

a7=9

a10=2

v5

v1

v9

a5=1

a2=4

a8=7

a11=4

v8

v3

a9=4

a3=5

a6=2

v4

v6


3858194

相关定义

ve(i) vi的最早开始时间=v1到vi的最长路径

ve(n)=工程完成时间

vl(i) vi的最迟开始时间=vn-vi到vn的最长路径

e(k) 活动ak的最早开始时间

若ak=<vi,vj> 则e(k)=ve(i)

l(k) 活动ak的最迟开始时间

若ak=<vi,vj> 则l(k)=vl(j)- ak

l(k)-e(k) 活动ak的松弛时间 slack time

若l(k)=e(k)则ak叫做关键活动


Ve i vl i

ve(i),vl(i)的递归算法(以拓扑排序递归)

ve(0)=0;

ve(j)=max{ ve(i)+dut<i,j>}

<i,j>∈T

dut<i,j> =边ak=<vi,vj>的长

T是所有以vj为终点的边的集合

vl(n-1)=ve(n-1)

vl(i)=min{vl(j)-dut<i,j>}

<i,j>∈S

S是所有以vi为起点的边的集合

由ve(i),vl(i)可以计算l(k),e(k)


3858194

v2

v7

a1=6

a4=1

a7=9

a10=2

v5

v1

v9

a5=1

a2=4

a8=7

a11=4

v8

v3

a3=5

a9=4

a6=2

v4

v6


3858194

关键路径的算法

修改拓扑排序算法,增加一个纪录拓扑排序求得的序列。用来逆向计算vl.


Template class t void criticalpath graph t g

Template<class T>void CriticalPath(Graph<T> G)

{ int i,j, l,ee,el,tag,n=G.NumberOfVertices;

int *indegree=new int[n];

int *ve=new int[n];

int *vl=new int[n];

Stack<int> S1,S2; Tu,v;

InDegree(G,indegree);//纪录顶点的入度

for(i=0;i<n;i++) if(indegree[i]==0)S.Push(i);

int count=0; //入度为0的点进栈


3858194

while(!S.StackEmpty( ))

{ i=S.Pop( ); //取出一个入度0的点输出

T.Push(i);

++count;

for(j=G.GetFirstNeighber(i); j>0;

j= G.GetNextNeighber(i,j))

if(--indegree[j]==0)S.Push(j); //邻接点入度减1

u=GetVertex(G,i); v=GetVertex(G,j);

l=ve[i]+G.GetWeight(u,v);

if(l>ve[j])ve[j]=l;

}

if(count<n)cout<<“There is a circle!”;

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

vl[i]=ve[i]; //初始化vl[I]


3858194

while(!T.StackEmpty( ))

for(i=T.Pop( ), j=G.GetFirstNeighber(i); j>0;

j= G.GetNextNeighber(i,j))

{ u=GetVertex(G,i); v=GetVertex(G,j);

l=vl[j]-G.GetWeight(u,v);

if(l<vl[i])vl[i]=l; }

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

for(j=G.GetFirstNeighber(i); j>0;

j= G.GetNextNeighber(i,j))

{ u=GetVertex(G,i); v=GetVertex(G,j);

l=G.GetWeight(u,v); ee=ve[i]; el=vl[j]-l;

tag= (ee==el)? ‘*’: ‘ ’;

cout<<u<<“ ”<<v<<“ ”

<<ee<<“ ”<<el<<“ ”<<tag<<endl;}

}


  • Login