1.28k likes | 1.47k Views
第八章 图. 图 图的存储表示 图的遍历 无向图的连通分量和生成树 最短路径 拓扑排序. 一、图. 图 应用最广泛的数据结构。 不同于树的另一种 非线性结构 每个顶点可以与 多个 其他顶点相关联,各顶 点之间的关系是 任意 的。 简单图 没有自身环,两点间至多一条边. v 1. v 3. v 2. v 1. v 5. v 3. v 4. v 4. v 2. 无向图. 有向图. 图的基本概念. G=<V, E>
E N D
第八章 图 • 图 • 图的存储表示 • 图的遍历 • 无向图的连通分量和生成树 • 最短路径 • 拓扑排序
一、图 图 应用最广泛的数据结构。 不同于树的另一种非线性结构 每个顶点可以与多个其他顶点相关联,各顶 点之间的关系是任意的。 简单图 没有自身环,两点间至多一条边 v1 v3 v2 v1 v5 v3 v4 v4 v2 无向图 有向图
图的基本概念 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 为整个图的总度,出度,入度数。
图的基本概念 路径 vi······vj, 以vi为起点vj为终点的顶点序列。 路径的长 路径上边的数目, 简单路径 顶点都不重复的路径, 回路 环 首尾相接的路径, 简单回路 除第一个和最后一个顶点以外都不重 复的路径, vivj连通 有路径 vi······vj, 连通图 任意两点都连通, 有向图 vivj强连通 vivj连通 vjvi也连通, 强连通图 任意两点都强连通。
v2 v2 v1 v1 v4 v4 v3 v3 v5 v5 弱连通 强连通
强连通分量:彼此强连通的顶点的子集 B A ABC D EFG H I D C F E H G I
完全图 任意两点间都有边相关联的图。 无向完全图 共有边 1/2(n*(n-1)) 条, 有向完全图 共有边 n(n-1) 条。 稀疏图 |E|<nlog n 稠密图 |E|<nlog n 带权边 具有边长的边 有权图 图的所有边都是带权边。 网络 有权图
子图 G=(V, E), G’=(V’, E’) 如果 V’ V, E’ E , 就称 G’是G的子图。 ∩ ∩ 连通分量 一个图的极大连通子图。 强连通分量 一个图的极大强连通子图。 连通图的生成树 含有所有顶点的极小 连通图 n个顶点尽可能少边的连通图有n-1条边。 非连通图的生成森林:所有k个连通分支的生成树组成生成森林,共有n-k条边。
有向树 有向图连通图恰有一个顶点的入度为0, 其余顶点的入度都是1。 有向图的生成森林: 有向图的一个子图,含有所有顶点,构成若干互不相交的有向树,叫做生成森林。
二、图的存储结构 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
无向图的邻接矩阵是对称矩阵 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
有向图的邻接矩阵 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
网的邻接矩阵 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
#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;
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);
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);
// 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);
// 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>;};
2. 邻接表 A B E C D 有向图可以有正反两种邻接表
邻接表表示的图的定义 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;} };
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);
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);
// 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);
// 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);};
//邻接矩阵表示的图的实现// 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;}
图的输入格式 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”);
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>::GraphEmpty(void) const{ return graphsize == 0;}
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;}
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];}
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;}
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;}
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;}
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是否在图的 顶点表中,有一个不在图中就给出 错误信息返回。 在图中,则确定位置pos1,pos2, 设置边(pos1,pos2)的权值。
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;}
删除一个顶点 如果顶点不在表中给出错误信息返回。 如果在表中,确定位置pos 把邻接矩阵分成四块 pos 第I块中边不动 第II块中边列左移 第III块中边行上移 第IV块中边 列左移行上移 I II pos III IV
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]; }
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;}
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 >VertexIterator<T>::VertexIterator(Graph<T>& G): SeqListIterator< T > (G.vertexList){}
深度优先搜索用递归算法 三、图的遍历 输出顶点并标记 循环:{ 递归计算第一个邻接点 (如 未标记) 下一个邻接点 } ABCDEIHF A F B C E H D I
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; }
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; }
//深度优先搜索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
//深度优先搜索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);
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}
广度优先搜索 用队列 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
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; }
四、无向图的连通分量和生成树 一个图中互相连通的点的极大子集叫连通分量。 从一点出发,深度优先或广度优先搜索到的子图就是连通分量。 从连通图的任意一点出发,深度优先搜索到的子图也就是图的生成树,也叫深度优先生成树;广度优先搜索到的生成树,也叫广度优先生成树; 非连通图遍历得到的是生成森林: 从一点出发深度优先搜索并标记,得到一棵树,再继续得到另一棵树,直到取遍所有顶点。