声明:本文内容来自《数据结构》一书,主编秦锋。版权归该书所有,仅供学习参考!
图的存储主要存储两个方面的内容:顶点集和边集。
图的存储不能采用顺序存储,因为图的结构不同于线性表和树,任意顶点之间可能存储在联系,不存在用存储位置实现数据元素间的映像关系,而必须显式地存储顶点之间的关系信息,一般有两种方法:
1.用边的集合表示图;
2.用链表关系表示图。
邻接矩阵属于第一类,而邻接表,邻接多重表等属于第二类。下面的源代码实现的是邻接表,详细请看源代码:
#include <iostream.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 4 #define MAX_NAME 2 typedef int Infortype; typedef int VertexType; typedef enum{ DG, DN, AG, AN } GraphKind; //图形种类{有向图,有向网,无向图,无向网} typedef struct _ArcNode { int adjvex; //该弧指向的顶点的位置 struct _ArcNode *nextarc; //指向下一条弧的指针 Infortype *info; //该弧的相关信息 }ArcNode; typedef struct _VNode { VertexType data; //顶点信息 ArcNode *firstarc; //指向第一条依附该顶点的狐的指针 }VNode, AdjList[MAX_VERTEX_NUM]; typedef struct _ALGraph { AdjList vertices; int vexnum, arcnum; //弧的当前顶点数和弧数 int kind; }ALGraph; //邻接表存储Graph类 class Graph { private: ALGraph G; public: //求顶点在图中的位置 int LocateVex( ALGraph G, VertexType u ); //构造没有相关信息的无向图 void CreateGraph( ALGraph &G ); //销毁图G void DestroyGraph( ALGraph &G ); //返回序号是v的顶点值 VertexType GetVex( ALGraph G, int v ); //对v赋新值value void PutVex( ALGraph &G, VertexType v, VertexType value ); //返回v的第一个邻接顶点的序号 int FirstAdjVex( ALGraph G, VertexType v ); //返回v的(相对于w的)下一个邻接顶点的序号 int NextAdjVex( ALGraph G, VertexType v, VertexType w ); //在图G中增加新顶点v void InsertVex( ALGraph &G, VertexType v ); //删除图G顶点v及其相关的弧 void DeleteVex( ALGraph &G, VertexType v ); //在G中增加弧<v,w> void InsertArc( ALGraph &G, VertexType v, VertexType w ); //在图G中删除弧<v,w> void DeleteArc( ALGraph &G, VertexType v, VertexType w ); //从第v个顶点出发递归深度优先遍历图G void DFS( ALGraph g, int v ); //从第1个顶点出发深度优先遍历图G void DFSTraverse( ALGraph G ); //从第1个顶点出发广度优先遍历图G void BFSTraverse( ALGraph G ); //输出图G void Display( ALGraph G ); }; /** * 初始条件:图G存在,u和G中定点有相同特征 * 操作结果:若G中存在顶点u,则返回该顶点在图中的位置 * 否则返回-1 */ int Graph::LocateVex( ALGraph G, VertexType u ) { int i; for( i=0; i < G.vexnum; i++ ) //if( strcmp( u, G.vertices[i].data ) == 0 ) if( u == G.vertices[i].data ) return i; return -1; } /** * * 采用邻接表存储结构,构造没有相关信息的图G * (用一个函数构造4种图) */ void Graph::CreateGraph( ALGraph &G ) { int i, j, k; int w; //权值 VertexType va = NULL, vb = NULL; ArcNode *p; cout << “请输入图的类型(有向图:0, 有向网:1, 无向图:2, 无向网3 ): ” << endl; cin >> G.kind; cout << “请输入图的顶点数,边数: ” << endl; cin >> G.vexnum >> G.arcnum; cout << “请输入” << G.vexnum << “个顶点的值(” << MAX_NAME << “个字符):” << endl; for( i = 0; i <G.vexnum; i++ ) //构造顶点向量 { cin >> G.vertices[i].data; G.vertices[i].firstarc = NULL; } if( G.kind == 1 || G.kind == 3 ) //网 cout << “请顺序输入每条弧(边)的权值,弧尾和弧头(以空格作为间隔):” << endl; else //图 cout << “请顺序输入每条弧边的弧尾和弧头(以空格作为间隔):” << endl; for( k = 0; k < G.arcnum; k++ ) //构造表结点链表 { if( G.kind == 1 || G.kind == 3 ) //网 cin >> w >> va >> vb; else //图 cin >> va >> vb; i = LocateVex( G, va ); //弧头 j = LocateVex( G, vb ); //弧尾 p = ( ArcNode * )malloc( sizeof( ArcNode ) ); p->adjvex = j; if( G.kind == 1 || G.kind == 3 ) { p->info = ( int * )malloc( sizeof( int ) ); *( p->info ) = w; } else p->info = NULL; p->nextarc = G.vertices[i].firstarc; //插在表头 G.vertices[i].firstarc = p; if( G.kind >= 2 ) //无向图或网,产生第二个表结点 { p = ( ArcNode* )malloc( sizeof( ArcNode ) ); p->adjvex = i; if( G.kind == 3 ) //无向图 { p->info = ( int * )malloc( sizeof( int ) ); *( p->info ) = w; } else p->info = NULL; //无向图 p->nextarc = G.vertices[j].firstarc; //插在表头 G.vertices[j].firstarc = p; } } } /** * 初始条件:图G存在 * 操作结果:销毁图G * */ void Graph::DestroyGraph( ALGraph &G ) { int i; ArcNode *p, *q; G.vexnum = 0; G.arcnum = 0; for( i = 0; i < G.vexnum; i++ ) { p = G.vertices[i].firstarc; while( p ) { q = p->nextarc; if( G.kind % 2 ) free( p->info ); free( p ); p = q; } } } /** * 初始条件:图G存在,v是G中某个顶点的序号 * 操作结果:若返回v值 * */ VertexType Graph::GetVex( ALGraph G, int v ) { if( v >= G.vexnum || v < 0 ) exit( 1 ); return G.vertices[v].data; } /** * 初始条件:图G存在,v是G中某个顶点 * 操作结果:对v赋新值value * */ void Graph::PutVex( ALGraph &G, VertexType v, VertexType value ) { int i; i = LocateVex( G, v ); if( i > -1 ) G.vertices[i].data = value; } /** * 初始条件:图G存在,v是G中某个顶点 * 操作结果:返回v的第一个邻接顶点的序号 * 若顶点在G中没有邻接顶点,则返回-1 */ int Graph::FirstAdjVex( ALGraph G, VertexType v ) { ArcNode *p; int v1; v1 = LocateVex( G,v ); p = G.vertices[v1].firstarc; if( p ) return p->adjvex; else return -1; } /** * 初始条件:图G存在,v是G中某个顶点,w是v的邻接顶点 * 操作结果:返回v的(相对于w的)下一个邻接顶点的序号。 * 若w是v的最后一个邻接点,则返回-1 */ int Graph::NextAdjVex( ALGraph G, VertexType v, VertexType w ) { ArcNode *p; int v1, w1; v1 = LocateVex( G, v ); //v1为顶点v在图G中的序号 w1 = LocateVex( G, w ); //w1为顶点w在图G中的序号 p = G.vertices[v1].firstarc; while( p && p->adjvex != w1 ) //指针p不空且所指针表结点不是w p = p->nextarc; if( !p || p->nextarc ) //没找到w或w是最后一个邻接顶点 return -1; else return p->nextarc->adjvex; } /** * 初始条件:图G存在,u和图中顶点又相同特征 * 操作结果:在图G中增添新顶点v(不增添与定点相关的弧,留待InsertArchive()去做) * */ void Graph::InsertVex( ALGraph &G, VertexType v ) { G.vertices[G.vexnum].data = v; //构造新顶点向量 G.vertices[G.vexnum].firstarc = NULL; G.vexnum++; //图G的顶点数加1 } /** * 初始条件:图G存在,v是G中某个顶点 * 操作结果:删除G中顶点v及其相关的弧 * */ void Graph::DeleteVex( ALGraph &G, VertexType v ) { int i, j; ArcNode *p, *q; j = LocateVex( G, v ); //j是定点v的序号 if( j < 0 ) //v不是图G的顶点 return; p = G.vertices[j].firstarc; //删除以v为出度的弧或边 while( p ) { q = p; p = p->nextarc; if( G.kind % 2 ) //网 free( q->info ); free( q ); G.arcnum–; //弧或边数减1 } G.arcnum–; //顶点数减1 for( i = j; i < G.vexnum; i++ ) //顶点v后面的顶点前移 G.vertices[i] = G.vertices[i+1]; for( i = 0; i < G.vexnum; i++ ) //删除以v为入度的弧或边且必要是修改表结点的顶点位置 { p = G.vertices[i].firstarc; //指向第1条弧或边 while( p ) //有弧 { if( p->adjvex == j ) { if( p == G.vertices[i].firstarc ) //待删结点是第1个结点 { G.vertices[i].firstarc = p->nextarc; if( G.kind%2 ) free( p->info ); free( p ); p = G.vertices[i].firstarc; if( G.kind < 2 ) G.arcnum–; } else { q->nextarc = p->nextarc; if( G.kind % 2 ) free( p->info ); free( p ); p = p->nextarc; if( G.kind < 2 ) //有向 G.arcnum–; //弧或边数减1 } } else { if( p->adjvex > j ) p->adjvex–; //修改表结点的顶点位置值 q = p; p = p->nextarc; } } } } /** * 初始条件:图G存在,v和w是G中两个顶点操作结果:在G中增添弧<v,w>, * 若G是无向的,则还增添对称弧<w,v> * */ void Graph::InsertArc( ALGraph &G, VertexType v, VertexType w ) { ArcNode *p; int w1, i, j; i = LocateVex( G, v); //弧尾或边的序号 j = LocateVex( G, w ); //弧头或边的序号 if( i < 0 || j < 0 ) return; G.arcnum++; if( G.kind % 2 ) { cout << “请输入弧(边)” << v << “->” << w << “的权值:” << endl; cin >> w1; } p = ( ArcNode * )malloc( sizeof( ArcNode ) ); p->adjvex = j; if( G.kind % 2 ) { p->info = ( int * )malloc( sizeof( int ) ); *( p->info ) = w1; } else p->info = NULL; p->nextarc = G.vertices[i].firstarc; //插在表头 G.vertices[i].firstarc = p; //无向,生产另一个表结点 if( G.kind >= 2 ) { p = ( ArcNode * )malloc( sizeof( ArcNode ) ); p->adjvex = i; if( G.kind == 3 ) { p->info = ( int* )malloc( sizeof( int ) ); *( p->info ) = w1; } else p->info = NULL; p->nextarc = G.vertices[j].firstarc; //插在表头 G.vertices[j].firstarc = p; } } /** * 初始条件:图G存在,v和w是G中两个顶点 * 操作结果:在G中删除弧<v,w>,若G是无向的,则还删除对称弧<w,v> * */ void Graph::DeleteArc( ALGraph &G, VertexType v, VertexType w ) { ArcNode *p, *q; int i, j; i = LocateVex( G, v ); //i是定点(弧尾)的序号 j = LocateVex( G, w ); //j是定点(弧头)的序号 if( i < 0 || j < 0 || i == j ) return; p = G.vertices[i].firstarc; //p指向顶点v的第一条弧尾 while( p && p->adjvex != j ) //p不空且所指之弧不是待删除<v,w> { //p指向下一条弧 q = p; p = p->nextarc; } if( p && p->adjvex == j ) //找到弧<v,w> { if( p == G.vertices[i].firstarc ) //p是指第1条弧 G.vertices[i].firstarc = p->nextarc; //指向下一条弧 else q->nextarc = p->nextarc; //指向下一条弧 if( G.kind % 2 ) free( p->info ); //释放此节点 free( p ); //弧或边数减1 G.arcnum–; } if( G.kind >= 2 ) //无向,删除对称弧<w,v> { p = G.vertices[j].firstarc; while( p && p->adjvex != i ) { q = p; p = p->nextarc; } if( p && p->adjvex == i ) { if( p == G.vertices[j].firstarc ) G.vertices[j].firstarc = p->nextarc; else q->nextarc = p->nextarc; if( G.kind == 3 ) free( p->info ); free( p ); } } } /** * 操作结果:输出邻接表G * */ void Graph::Display( ALGraph G ) { int i; ArcNode *p; switch( G.kind ) { case DG: cout << “有向图” << endl; break; case DN: cout << “有向网” << endl; break; case AG: cout << “无向图” << endl; break; case AN: cout << “无向网” << endl; break; } cout << G.vexnum << “个顶点:” << endl; for( i = 0; i < G.vexnum; i++ ) cout << G.vertices[i].data; cout << endl << G.arcnum << “条弧(边): ” << endl; for( i = 0; i < G.vexnum; i++ ) { p = G.vertices[i].firstarc; while( p ) { if( G.kind <= 1 ) { cout << G.vertices[i].data << “->” << G.vertices[p->adjvex].data; if( G.kind == DN ) cout << “: ” << *(p->info); } else { if( i < p->adjvex ) { cout << G.vertices[i].data << “->” << G.vertices[p->adjvex].data; if( G.kind == DN ) cout << “: ” << *(p->info); } } p = p->nextarc; } cout << endl; } } /** * 遍历,从第v个顶点出发递归深度优先遍历图G * 需要申请一个数组作为标志 */ bool g_Bvisited[MAX_VERTEX_NUM]; void Graph::DFS( ALGraph G, int v ) { VertexType w1,v1; int w; g_Bvisited[v] = true; cout << G.vertices[v].data << endl; v1 = GetVex( G, v ); for( w = FirstAdjVex( G, v1 ); w >= 0; w = NextAdjVex( G, v1, w1 = GetVex( G, w ) ) ) if( !g_Bvisited[w] ) DFS( G, w ); } int main( void ) { Graph g; ALGraph G; g.CreateGraph( G ); g.Display( G ); return 0; }