==引言:==这一次我们来学习数据结构最后一部分,同时也是我觉得最难的一部分——图的学习与应用。
声明:定义都是书中所写。
图(一)
一.图的定义
定义为:图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
注意:
1.在图中的数据元素我们可以用顶点来表示。
2.图中的任意两个顶点都有可能存在某种关系,我们用边来表示。
二.各种图的定义
1.简单图
定义:在图中,若不存在顶点到其自身的边,且同一条边不重复出现,则称这样的图为简单图。
2.稠密图与稀疏图
定义:有很少条边或弧的图为稀疏图,反之为稠密图。
3.有向图与无向图
(1)有向边
若从顶点Vi到Vj的边有方向,则称这条边为有向边,也叫作弧。一般我们用<>来表示。
(2)无向边
若从顶点Vi到Vj的边没有方向,则称这条边为无向边。一般我们用()来表示。
了解了有向边与无向边的定义,那我们就可以知道有向图与无向图的定义。
三.图的相关术语
1.权
定义:有些图中的边具有相关的数字,这种我们称之为权。
这些带权值的图又称之为网。
2.度
定义:无向图顶点的边数叫做度,有向图的顶点分为入度与出度。
3.连通与环
图中顶点之间存在路径,如果两个顶点存在路径,则称这两个图是连通的。
如果路径从起点出发,最终回到起点位置,我们称为环。
四.图的实现
这里只给出无向图的代码,有向图在就是在有向图的代码上进行细小改动。
1.邻接矩阵
(1)定义
图的邻接矩阵设是用两个数组来存放。一个一维数组存放图中顶点的信息,一个二维数组存放图中边或弧的信息。
(2)无向图的邻接矩阵实现
定义:
struct Tu
{
char vecs[N];//顶点表
int arc[N][N];//边表
int numVec,numEdges;//顶点数与边数
};
实现:
void creat(struct Tu *p)
{
int i,j,k,w;
cout<<"请输入顶点数与边数"<<endl;
cin>>p->numVec>>p->numEdges;
cout<<"请输入顶点"<<endl;
for(i=0;i<p->numVec;i++)
cin>>p->vecs[i];
for(i=0;i<p->numVec;i++)//初始化邻接矩阵
for(j=0;j<p->numVec;j++)
p->arc[i][j]=max;
for(k=0;k<p->numEdges;k++)
{
cout<<"请输入行与列,还有权值"<<endl;
cin>>i>>j>>w;
p->arc[i][j]=w;
p->arc[j][i]=p->arc[i][j];//无向图是中心对称的图
}
}
完整代码:
#include <iostream>
using namespace std;
#define N 103 //用来表示最大顶点数
#define max 65536 //用来表示权值最大,也就是正无穷
struct Tu
{
char vecs[N];//顶点表
int arc[N][N];//边表
int numVec,numEdges;//顶点数与边数
};
void creat(struct Tu *p)
{
int i,j,k,w;
cout<<"请输入顶点数与边数"<<endl;
cin>>p->numVec>>p->numEdges;
cout<<"请输入顶点"<<endl;
for(i=0;i<p->numVec;i++)
cin>>p->vecs[i];
for(i=0;i<p->numVec;i++)//初始化邻接矩阵
for(j=0;j<p->numVec;j++)
p->arc[i][j]=max;
for(k=0;k<p->numEdges;k++)
{
cout<<"请输入行与列,还有权值"<<endl;
cin>>i>>j>>w;
p->arc[i][j]=w;
p->arc[j][i]=p->arc[i][j];//无向图是中心对称的图
}
}
int main()
{
struct Tu S;
S.numVec=0;
S.numEdges=0;
int j,i;
creat(&S);
for(i=0;i<S.numVec;i++)
{
for(j=0;j<S.numVec;j++)
cout<<S.arc[i][j]<<" ";
cout<<endl;
}
return 0;
}
2.邻接表
(1)定义
在邻接矩阵中,我们发现存在空间浪费问题,并且我们发现创建图的时间复杂度是O(n*n)。有联想到线性表知识,我们决定用链表来实现图。我们把数组与链表相结合的存储方式称之为邻接表。
(2)无向图的邻接表实现
邻接表的插入像极了链表中的头插法。
定义:
#define N 103 //用来表示最大顶点数
#define max 65536 //用来表示权值最大,也就是正无穷
struct Edge//边表节点
{
int adjvex;//邻接域点,存放该点对应下表
int weigth;//权值
struct Edge *next;//指针域
};
struct Vec//顶点表节点
{
char data;//顶点域
struct Edge *first;//边表节点
};
struct Tu
{
struct Vec tulist[N];
int numVec,numEdge;//顶点数与边数
};
实现:
void creat(struct Tu *p)
{
int i,j,w,k;
struct Edge *e;
cout<<"请输入顶点数与边数"<<endl;
cin>>p->numVec>>p->numEdge;
cout<<"请输入顶点信息"<<endl;
for(i=0;i<p->numVec;i++)
{
cin>>p->tulist[i].data;
p->tulist[i].first=NULL;//将边表置空
}
for(k=0;k<p->numEdge;k++)
{
cout<<"请输入行与列,还有权值"<<endl;
cin>>i>>j>>w;
e=new(struct Edge);
e->adjvex=j;
e->weigth=w;
e->next=p->tulist[i].first;
p->tulist[i].first=e;
e=new(struct Edge);
e->adjvex=i;
e->weigth=w;
e->next=p->tulist[j].first;
p->tulist[j].first=e;
}
}
完整代码:
#include <iostream>
using namespace std;
#define N 103 //用来表示最大顶点数
#define max 65536 //用来表示权值最大,也就是正无穷
struct Edge//边表节点
{
int adjvex;//邻接域点,存放该点对应下表
int weigth;//权值
struct Edge *next;//指针域
};
struct Vec//顶点表节点
{
char data;//顶点域
struct Edge *first;//边表节点
};
struct Tu
{
struct Vec tulist[N];
int numVec,numEdge;//顶点数与边数
};
void creat(struct Tu *p)
{
int i,j,w,k;
struct Edge *e;
cout<<"请输入顶点数与边数"<<endl;
cin>>p->numVec>>p->numEdge;
cout<<"请输入顶点信息"<<endl;
for(i=0;i<p->numVec;i++)
{
cin>>p->tulist[i].data;
p->tulist[i].first=NULL;//将边表置空
}
for(k=0;k<p->numEdge;k++)
{
cout<<"请输入行与列,还有权值"<<endl;
cin>>i>>j>>w;
e=new(struct Edge);
e->adjvex=j;
e->weigth=w;
e->next=p->tulist[i].first;
p->tulist[i].first=e;
e=new(struct Edge);
e->adjvex=i;
e->weigth=w;
e->next=p->tulist[j].first;
p->tulist[j].first=e;
}
}
void out(struct Tu *q)
{
int n,m,j,k,l,i;
struct Tu *r;
struct Edge *s;
r=q;
for(i=0;i<r->numVec;i++)
{
cout<<"节点数 "<<r->tulist[i].data<<endl;
if(r->tulist[i].first!=NULL)
{
s=r->tulist[i].first;
while(s!=NULL)
{
cout<<"->"<<s->adjvex<<" ";
cout<<s->weigth;
s=s->next;
}
cout<<endl;
}
}
}
int main()
{
struct Tu S;
S.numEdge=0;
S.numVec=0;
creat(&S);
out(&S);
return 0;
}
五.后记
由于图的知识点很多,然后还有点难理解,所以这次的图分为两部分,下一部分在进行图遍历的学习。