一、概念
1.图、树、线性结构区别:
图(Graph)是一种比线性表和树更为复杂的数据结构。
图结构:是研究数据元素之间的多对多的关系。在这种结构中,任意两个元素之间可能存在关系。即结点之间的关系可以是任意的,图中任意元素之间都可能相关。
树结构:是研究数据元素之间的一对多的关系。在这种结构中,每个元素对下(层)可以有0个或多个元素相联系,对上(层)只有唯一的一个元素相关,数据元素之间有明显的层次关系。
线性结构:是研究数据元素之间的 一对一关系。在这种结构中,除第一个和最后一个元素外,任何一个元素都有唯一的一个直接前驱和直接后继。
二、图的定义和术语
一个图(G)定义为一个偶对(V,E) ,记为G=(V,E)。
其中: V是顶点(Vertex)的非空有限集合,记为V(G);E是无序集V&V的一个子集,记为E(G) ,其元素是图的弧(Arc)。
将顶点集合为空的图称为空图。
其形式化定义为:
G=(V ,E)
V={v|vÎdataobject}
E={<v,w>|v,wÎV∧p(v,w)}
P(v,w)表示从顶点v到顶点w有一条直接通路。在无向图中,若"<v,w>ÎE(G) ,有<w,v>ÎE(G) ,即E(G)是对称,则用无序对(v,w) 表示v和w之间的一条边(Edge),因此(v,w)和(w,v)代表的是同一条边。
例1:设有有向图G1和无向图G2,形式化定义分别是:
G1=(V1 ,E1)
V1={a,b,c,d,e}
E1={<a,b>,<a,c>, <a,e>,<c,d>,<c,e> ,<d,a>,<d,b>,<e,d>}
G2=(V2 ,E2)
V2={a,b,c,d}
E2={(a,b), (a,c), (a,d), (b,d), (b,c), (c,d)}
它们所对应的图如图7-1所示
完全无向图:对于无向图,若图中顶点数为n,用e表示边的数目,则eÎ[0,n(n-1)/2]。具有n(n-1)/2条边的无向图称为完全无向图。
完全无向图另外的定义是:
对于无向图G=(V,E),若"vi,vjÎV,当vi≠vj时,有(vi ,vj)ÎE,即图中任意两个不同的顶点间都有一条无向边,这样的无向图称为完全无向图。
完全有向图:对于有向图,若图中顶点数为n ,用e表示弧的数目,则eÎ[0,n(n-1)] 。具有n(n-1)条边的有向图称为完全有向图。
完全有向图另外的定义是:
对于有向图G=(V,E),若"vi,vjÎV,当vi≠vj时,有<vi ,vj>ÎE∧<vj,vi>ÎE ,即图中任意两个不同的顶点间都有一条弧,这样的有向图称为完全有向图。
有很少边或弧的图(e<n㏒n)的图称为稀疏图,反之称为稠密图。
权(Weight):与图的边和弧相关的数。权可以表示从一个顶点到另一个顶点的距离或耗费。
子图和生成子图:设有图G=(V,E)和G’=(V’,E’),若V’ÌV且E’ÌE,则称图G’是G的子图;若V’=V且E’ÌE,则称图G’是G的一个生成子图。
顶点的邻接(Adjacent):对于无向图G=(V,E),若边(v,w)ÎE,则称顶点v和w 互为邻接点,即v和w相邻接。边(v,w)依附(incident)与顶点v和w 。对于有向图G=(V,E),若有向弧<v,w>ÎE,则称顶点v“邻接到”顶点w,顶点w“邻接自”顶点v,弧<v,w>与顶点v和w“相关联” 。
顶点的度、入度、出度:对于无向图G=(V,E),"viÎV,图G中依附于vi的边的数目称为顶点vi的度(degree),记为TD(vi)。
显然,在无向图中,所有顶点度的和是图中边的2倍。即 ∑TD(vi)=2e i=1, 2, …, n ,e为图的边数。
对有向图G=(V,E),若"vi ÎV ,图G中以vi作为起点的有向边(弧)的数目称为顶点vi的出度(Outdegree),记为OD(vi) ;以vi作为终点的有向边(弧)的数目称为顶点vi的入度(Indegree),记为ID(vi) 。顶点vi的出度与入度之和称为vi的度,记为TD(vi) 。即
TD(vi)=OD(vi)+ID(vi)
路径(Path)、路径长度、回路(Cycle) :对无向图G=(V,E),若从顶点vi经过若干条边能到达vj,称顶点vi和vj是连通的,又称顶点vi到vj有路径。
对有向图G=(V,E),从顶点vi到vj有有向路径,指的是从顶点vi经过若干条有向边(弧)能到达vj。
三、图的抽象数据类型定义
图是一种数据结构,加上一组基本操作就构成了图的抽象数据类型。
图的抽象数据类型定义如下:ADTGraph{
数据对象V:具有相同特性的数据元素的集合,称为顶点集。
数据关系R:R={VR}
VR={<v,w>|<v,w>|v,wÎV∧p(v,w),<v,w>表示从v到w的弧,P(v,w)定义了弧<v,w>的信息
}
四、实现代码:
Graph.h
#ifndef __CHelloWorld__Graph__
#define __CHelloWorld__Graph__
#include <iostream>
#define MaxNum 20 //图的最大顶点数
#define MaxValue 65535 //最大值(可设为一个最大整数)
typedef struct
{
char Vertex[MaxNum]; //保存顶点信息(序号或字母)
int GType; //图的类弄(0:无向图,1:有向图)
int VertexNum; //顶点的数量
int EdgeNum; //边的数量
int EdgeWeight[MaxNum][MaxNum]; //保存边的权
int isTrav[MaxNum]; //遍历标志
}GraphMatrix;
class Graph
{
public:
void CreateGraph(GraphMatrix *GM); //创建邻接矩阵图
void ClearGraph(GraphMatrix *GM); //清空矩阵
void OutGraph(GraphMatrix *GM); //输出邻接矩阵
void DeepTraOne(GraphMatrix *GM,int n); //从第n个结点开始,深度遍历图
void DeepTraGraph(GraphMatrix *GM); //深度优化遍历
};
#endif /* defined(__CHelloWorld__Graph__) */
Graph.cpp
#include "Graph.h"
void Graph::CreateGraph(GraphMatrix *GM)
{
int i,j,k;
int weight;
char EstartV,EendV;
std::cout<<"输入图中各顶点信息\n";
for (i = 0; i<GM->VertexNum; i++)
{
getchar();
printf("第%d个顶点:",i+1);
scanf("%c",&(GM->Vertex[i]));
}
std::cout<<"输入构成各边的顶点及权值:\n";
for (k = 0; k <GM->EdgeNum; k++)
{
getchar();
printf("第%d条边:",k+1);
scanf("%c %c %d",&EstartV,&EendV,&weight);
for ( i= 0; EstartV != GM->Vertex[i]; i++);
for ( j= 0; EendV != GM->Vertex[j]; j++);
GM->EdgeWeight[i][j] = weight;
if (GM->GType == 0) //若是无向图
{
GM->EdgeWeight[j][i] = weight;
}
}
}
void Graph::ClearGraph(GraphMatrix *GM)
{
int i,j;
for (i=0; i<GM->VertexNum; i++)
{
for (j=0; j<GM->VertexNum; j++)
{
GM->EdgeWeight[i][j] = MaxValue;
}
}
}
void Graph::OutGraph(GraphMatrix *GM)
{
int i,j;
for (j=0; j<GM->VertexNum; j++)
{
printf("\t%c",GM->Vertex[j]);
}
std::cout<<"\n";
for (i=0; i<GM->VertexNum; i++)
{
printf("%c",GM->Vertex[i]);
for (j=0; j<GM->VertexNum; j++)
{
if (GM->EdgeWeight[i][j] == MaxValue)
{
printf("\tZ"); //以Z表示无穷大
}
else
{
printf("\t%d",GM->EdgeWeight[i][j]); //输出边的权值
}
}
std::cout<<"\n";
}
}
void Graph::DeepTraOne(GraphMatrix *GM, int n)
{
int i;
GM->isTrav[n] = 1; //标记该顶点已处理过
printf("\t%c",GM->Vertex[n]);
for (i=0; i<GM->VertexNum; i++)
{
if (GM->EdgeWeight[n][i] != MaxValue && !GM->isTrav[n])
{
DeepTraOne(GM, i);
}
}
}
void Graph::DeepTraGraph(GraphMatrix *GM)
{
int i;
for (i = 0; i<GM->VertexNum; i++)
{
GM->isTrav[i] = 0;
}
std::cout<<"深度优先遍历结点";
for (i=0; i<GM->VertexNum; i++)
{
if (!GM->isTrav[i])
{
DeepTraOne(GM, i);
}
}
std::cout<<"\n";
}
main.cpp
#include <iostream>
#include "Graph.h"
using namespace std;
int main(int argc, const char * argv[])
{
GraphMatrix GM;
Graph *myGraph = new Graph();
std::cout<<"输入生成图的类型:";
scanf("%d",&GM.GType);
std::cout<<"输入生成图的数量:";
scanf("%d",&GM.VertexNum);
std::cout<<"输入图的边数量:";
scanf("%d",&GM.EdgeNum);
myGraph->ClearGraph(&GM);
myGraph->CreateGraph(&GM);
std::cout<<"该图的邻接矩阵数据如下:\n";
myGraph->OutGraph(&GM);
myGraph->DeepTraGraph(&GM);
// std::cout << "Hello, World!\n";
return 0;
}
演示效果图:
示例图:
参考书籍:《C/C++常用算法手册》 《数据结构-清华大学严蔚敏》