这一篇简单介绍下一种更为复杂的数据结构------图,图是一种比线性表和树更为复杂的数据结构。图结构是研究数据与数据之间多对多的关系。
一、图的基本概念
1、一个图定义为一个偶对(V,E),记为G=(V,E);其中V是顶点的非空有限集合,记为V(G),E是无序集V&V的一个子集,记为E(G),其元素是图的弧。
2、弧:表示两个顶点之间存在一个关系,用顶点偶对<v,w>表示,通常根据顶点偶对将图分为有向图和无向图。
3、有向图:若图的关系集合E(G)中,顶点偶对<v,w>的v和w之间是有序的,称图G是有向图。在有向图中,若<v,w>属于E(G),表示从顶点v到顶点w有一条弧。
4、无向图:若图的关系集合E(G)中,顶点偶对<v,w>的v和w之间是无序的,称图G是无向图。在无向图中,E(G)是对称的,则用无序对(v,w)表示v到w的一条边,即(v,w)和(w,v)表示的是同一条边。
5、完全有向图:对于有向图,若图的顶点数是n,用e表示弧的数目,e=[0,n(n-1)],具有n(n-1)条弧的有向图称为完全有向图。
6、权:与图的边和弧相关的数。权可以表示从一个顶点到另一个顶点的距离或耗费。
7、子图和生成子图:设有两个图G1和G2,G1的顶点集是G2的顶点集的子集,G1的弧集是G2的弧集的子集,则称G1是G2的子图;若G1和G2的顶点集相等,G1的弧集是G2的弧集的子集,则称G1是G2的生成子图。
8、顶点的邻接:对于无向图G=(V,E),若v和w之间存在边,则称v和w互为邻接点。边依附于点v和w。对于有向图G=(V,E),若v和w之间存在有向弧,称顶点v邻接到w,称顶点w邻接自v,弧与顶点v和w相关联
9、度、出度、入度、路径(有向路径)、路径长度、回路(环)
10、连通图、非连通图、图的连通分量(非连通图的极大连通子图)-----无向图而言
11、强连通图、非强连通图、强连通分量-------有向图而言
12、生成树:一个连通图(无向图)的生成树是一个极小连通子图,包含全部n个顶点和构成树的n-1条边。生成森林:由若干棵有向树组成,含有图中全部顶点,有向树是只有一个顶点的入度为0,其他顶点的入度为1的有向图。
13、网:带权的连通图
二、图的存储结构
1、邻接矩阵(数组)表示法:包括图的创建、顶点定位、插入顶点、插入弧等
//图的邻接矩阵表示
#define INFINITY 1000
#define MAX_VEX 30 //最大顶点数目
using namespace std;
typedef string GraphKind;//有向,无向,带权有向,带权无向;(DG,AG,WDG,WAG)
//typedef ArcType AdjType;
typedef char VexType;
typedef int ArcValType;
typedef int ArcInfoType;
//弧或边的结构定义
typedef struct ArcType
{
VexType vex1,vex2; //依附的两个顶点;
ArcValType ArcVal;//权值
ArcInfoType ArcInfo;// 其他信息
}ArcType;
typedef ArcType AdjType;
//图的定义
typedef struct MGraph
{
GraphKind kind;
int vexnum,arcnum; //图的当前顶点数和弧数
VexType vexs[MAX_VEX];
AdjType adj[MAX_VEX][MAX_VEX];//弧集
}MGraph;
//create graph
void Create_Graph(MGraph *G)
{
cout<<"请输入图的种类标志:\n"<<endl;
string s;
cin>>s;
G->kind=s;
G->vexnum=0;
}
//图的顶点定位,即确定一个顶点在vexs数组中的下标
int Locate_Graph_Vex(MGraph *G,VexType *vex)
{
for(int k=0;k<G->vexnum;++k)
if(G->vexs[k]==*vex)
return k;
return -1;
}
//向图中增加顶点
int AddVex_Graph(MGraph *G,VexType *vex)
{
int k;
if(G->vexnum>=MAX_VEX){
cout<<"graph is overflow!"<<endl;
return -1;
}
if(Locate_Graph_Vex(G,vex)!=-1)
{
cout<<"the vex has existed!"<<endl;
return -1;
}
k=G->vexnum;
G->vexs[G->vexnum++]=*vex;
if(G->kind=="DG" || G->kind=="AG") //无权值
{
for(int j=0;j<G->vexnum;j++)
{
G->adj[j][k].ArcVal=G->adj[k][j].ArcVal=0;
}
}
else
{
for(int j=0;j<G->vexnum;j++)
{
G->adj[j][k].ArcVal=INFINITY;
G->adj[k][j].ArcVal=INFINITY;
}
}
return 1;
}
//
int AddArc_Graph(MGraph *G,ArcType *arc)
{
int k,j;
k=Locate_Graph_Vex(G,&arc->vex1);
j=Locate_Graph_Vex(G,&arc->vex2);
if(k==-1 || j==-1)
{
cout<<"the vexs has not existed!"<<endl;
return -1;
}
if(G->kind=="DG" || G->kind=="WDG") //y有向图或者带权的有向图
{
G->adj[k][j].ArcVal=arc->ArcVal;
G->adj[k][j].ArcInfo=arc->ArcInfo;
}
else //无向图或无权的无向图
{
G->adj[j][k].ArcVal=G->adj[k][j].ArcVal=arc->ArcVal;
G->adj[j][k].ArcInfo=G->adj[k][j].ArcInfo=arc->ArcInfo;
}
return 1;
}
这一篇介绍了基本概念和图的邻接矩阵的实现,下边贴出来测试代码,下一篇介绍图的邻接链表的实现和图的两种基本遍历方式。
#include <iostream>
#include <malloc.h>
#include "SeqGraph.h"
using namespace std;
int main()
{
cout << "Graph:" << endl;
MGraph *G;
VexType vexs[4]={'A','B','C','D'};
ArcType arc[4];
/*
for(int i=0;i<4;i++)
{
arc[i].ArcInfo=0;
arc[i].ArcVal=i+1;
arc[i].vex1=vex[i];
arc[i].vex2=vex[(i+1)%4];
}
*/
G=(MGraph *)malloc(sizeof(MGraph));
Create_Graph(G);
for(int i=0;i<4;i++)
{
AddVex_Graph(G,&vexs[i]);
}
for(int i=0;i<4;i++)
{
arc[i].ArcInfo=0;
arc[i].ArcVal=i+1;
arc[i].vex1=vexs[i];
arc[i].vex2=vexs[(i+1)%4];
AddArc_Graph(G,&arc[i]);
}
for(int i=0;i<4;i++)
{
cout<<G->vexs[i]<<" ";
}
cout<<endl;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++){
cout<<G->adj[i][j].ArcVal<<" ";
}
cout<<endl;
}
return 0;
}