图(Graph)的概念:
是由顶点的集合和顶点之间边的集合组成,通常表示为:
G ( V , E )
其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。在图中的数据元素,我们称之为顶点(Vertex),顶点集合有穷非空。在图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的。
简单图:在图中,若不存在顶点到其自身的边,且同一条边不重复出现。
邻接、依附:
无向图中,对于任意两个顶点vi和顶点vj,若存在边(vi,vj),则称顶点vi和顶点vj互为邻接点,同时称边(vi,vj)依附于顶点vi和顶点vj。
无向完全图:在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。
有向完全图:在有向图中,如果任意两个顶点之间都存在方向相反的两条弧,则称该图为有向完全图。
稀疏图:称边数很少的图为稀疏图;
稠密图:称边数很多的图为稠密图。
顶点的度:在无向图中,顶点v的度是指依附于该顶点的边数,通常记为TD (v)。
顶点的入度:在有向图中,顶点v的入度是指以该顶点为弧头的弧的数目,记为ID (v);
顶点的出度:在有向图中,顶点v的出度是指以该顶点为弧尾的弧的数目,记为OD (v)。
权:是指对边赋予的有意义的数值量。
网:边上带权的图,也称网图。
回路(环):第一个顶点和最后一个顶点相同的路径。
简单路径:序列中顶点不重复出现的路径。
简单回路(简单环):除了第一个顶点和最后一个顶点外,其余顶点不重复出现的回路。
连通图:在无向图中,如果从一个顶点vi到另一个顶点vj(i≠j)有路径,则称顶点vi和vj是连通的。如果图中任意两个顶点都是连通的,则称该图是连通图。
连通分量:非连通图的极大连通子图称为连通分量。
强连通图:在有向图中,对图中任意一对顶点vi和vj (i≠j),若从顶点vi到顶点vj和从顶点vj到顶点vi均有路径,则称该有向图是强连通图。
强连通分量:非强连通图的极大强连通子图。
生成树:n个顶点的连通图G的生成树是包含G中全部顶点的一个极小连通子图。
生成森林:在非连通图中,由每个连通分量都可以得到一棵生成树,这些连通分量的生成树就组成了一个非连通图的生成森林
基本操作的代码如下:
#include<iostream>
#include<cstdlib>
using namespace std;
#define MAXV 20
#define INF 65535
//图的邻接矩阵的定义
typedef int infoType;
typedef struct {
int no; //顶点的编号
infoType info;//顶点的其他的信息
}VertexType;
typedef struct {
int edges[MAXV][MAXV];//邻接矩阵数组
VertexType ves[MAXV];//存放顶点信息数组
int n; //存放顶点的个数
int e; //存放边的条数
}MatGraph; //邻接矩阵MatGraph
//建立无向网的邻接矩阵表示 //自己输入数据
void CreateMatGraph(MatGraph *&s){
int i,j,k,w;
cout<<"输入顶点数和边数"<<endl;
cin>>s->e;
cin>>s->n;
cout<<"输入顶点信息建立顶点表"<<endl;
for(i=0;i<s->n;i++){//输入顶点信息建立顶点表
cin>>s->ves[i].no;
cin>>s->ves[i].info;
}
cout<<"输入邻接矩阵初始化"<<endl;
for(i=0;i<s->n;i++){
for(j=0;j<s->n;j++)
if(i==j){
s->edges[i][j]=0;
}
else
{s->edges[i][j]=INF;}//输入邻接矩阵初始化
}
for(k=0;k<s->e;k++){
cin>>i>>j>>w;//输入下标i,j和权值
s->edges[i][j]=w;
s->edges[j][i]=w;
}
}
//邻接表的存储类型
typedef struct ANode{
int adjvex;//该边的邻接点编号
struct ANode *nextarc;// 指向下一条边的指针
int weight;//改变的相关信息权值;
}ArcNode;
typedef struct VNode{
infoType info; //顶点的其他信息
ArcNode *fristArcNode; //指向第一个边结点
}VNode;
typedef struct {
VNode ves[MAXV];//存放头节点信息数组
int n; //存放顶点的个数
int e; //存放边的条数
}AdjGraph;
//用邻接矩阵数组创建图
void CreateAdj(AdjGraph *&G,int A[MAXV][MAXV],int n,int e){
int i,j;
ArcNode *p;
G=(AdjGraph *)malloc(sizeof(AdjGraph));
for(i=0;i<n;i++){
G->ves[i].fristArcNode=NULL;
}
for(i=0;i<n;i++){ //检查邻接表中的每一个元素
for(j=n-1;j>=0;j++){
if(A[i][j]!=0&&A[i][j]!=INF){ //存在一条边
p=(ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点
p->adjvex=j; //存放邻接表
p->weight=A[i][j]; //存放权值
p->nextarc=G->ves[i].fristArcNode;
G->ves[i].fristArcNode=p;//头插法插入结点
}
}
}
G->n=n;
G->e=e;
}
//输出图的算法
void DispAdj(AdjGraph* G) { //输出邻接表G
int i;
ArcNode *p;
for(i=0;i<G->n;i++){
p=G->ves[i].fristArcNode;
cout<<i; //输出第i个结点信息
if(p!=NULL){
cout<<p->adjvex<<p->weight; //输出该第i个结点邻接点编号和权值
p=p->nextarc; //指向下一个邻接点
}
cout<<endl;
}
}
void DestroyAdj(AdjGraph *G) {
int i;
ArcNode *pre,*p;
for(i=0;i<G->n;i++){ //扫描所有头结点的 链表
pre=G->ves[i].fristArcNode;//pre指向第i个元素的头节点
if(pre!=NULL){
p=pre->nextarc; //p指向第i个元素的头节点的下一个结点
while(p!=NULL){//下一个结点不为空
free(pre); //释放前一个结点
pre=p; //结点后移
p=p->nextarc;
}
free(pre);
}
}
free(G); //释放头节点
}
//设计一个方法把邻接矩阵转化成邻接表
void MatTolist(MatGraph g,AdjGraph *&G){
int i,j;
ArcNode *p;
G=(AdjGraph *)malloc(sizeof(AdjGraph));
for(i=0;i<g.n;i++){
G->ves[i].fristArcNode=NULL; //头节点指向空
}
for(i=0;i<g.n;i++){ //检查邻接表中的每一个元素
for(j=g.n-1;j>=0;j++){
if(g.edges[i][j]!=0&&g.edges[i][j]!=INF){ //存在一条边
p=(ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点
p->adjvex=j; //存放邻接表
p->weight=g.edges[i][j]; //存放权值
p->nextarc=G->ves[i].fristArcNode;
G->ves[i].fristArcNode=p;//头插法插入结点
}
}
}
G->n=g.n;
G->e=g.e;
}
//邻接表转化成邻接矩阵
void ListToMat(MatGraph g,AdjGraph *&G) {
int i;
ArcNode *p;
for(i=0;i<G->n;i++){
p=G->ves[i].fristArcNode;
while(p!=NULL){
g.edges[i][p->adjvex]=p->weight;
p=p->nextarc;
}
}
G->n=g.n;
G->e=g.e;
}
//邻接表的深度优先遍历
int visited[MAXV]={0} ; //全局数组,用于标志位
void DFS(AdjGraph *G,int v){
ArcNode *p;
visited[v]=1; //标记已经访问的了
cout<<v; //输出被访问的编号
p=G->ves[v].fristArcNode; //p指向顶点的第一个邻接点
while(p!=NULL){
if(visited[p->adjvex]==0){
DFS(G,p->adjvex); //若p->adjvec未被访问则递归访问它
p=p->nextarc;//p指向下一个邻接点
}
}
}
//非连通同的深度优先遍历
void DFSI(AdjGraph *G) {
int i;
for(i=0;i<G->n;i++){
if(visited[i]==0){
DFS(G,i);
}
}
}
//邻接表的广度优先遍历
#define MAXSIZE 50
typedef struct {
int data[MAXSIZE]; //存放队中的元素
int front,rear; //队头和队尾指针
}SqQueue; //顺序队列
void initQueue(SqQueue *&q){ //初始化队列
q=(SqQueue *)malloc(sizeof(SqQueue));
q->front=q->rear=0; //队头和队尾都为0
}
bool enQueue(SqQueue *&q,int e){ //进队列
if((q->rear+1)%MAXSIZE==q->front){
return false;
}
q->rear=(q->rear+1)%MAXSIZE;
q->data[q->rear]=e;
return true;
}
bool QueueEmpty(SqQueue *q){ //判断队列是否为空
return(q->rear==q->front);
}
bool deQueue(SqQueue *&q,int &e){ //出队列
if(q->front==q->rear)
return false;
q->front++;
e=q->data[q->front];
return true;
}
void BFS(AdjGraph *G,int v){
int w,i;
ArcNode *p;
SqQueue *qu;
initQueue(qu);//初始化队列
int visited[MAXV];//定义顶点访问标记数组
for(i=0;i<G->n;i++){
visited[i]=0; //访问标记数组初始化
}
cout<<v;//
visited[v]=1;
enQueue(qu,v);
while(!QueueEmpty(qu)){ //队列不空循环
deQueue(qu,w); //出队一个顶点w
p=G->ves[w].fristArcNode; //指向w的第一个邻接点
while(p!=NULL){ //查询w所有的邻接点
if(visited[p->adjvex]==0){ //若当前的邻接点没有被访问
cout<<p->adjvex; //访问该邻接点
visited[p->adjvex]==1; //标记为置为1
enQueue(qu,p->adjvex); //该顶点进队列
}
p=p->nextarc; //p指向下个邻接点
}
}
cout<<endl;
}
//非连通同的广度优先遍历
void DFSI(AdjGraph *G) {
int i;
for(i=0;i<G->n;i++){
if(visited[i]==0){
BFS(G,i);
}
}
}
//判断无向图是否连通
bool Connect(AdjGraph *G){
int i;
bool flag=true; //标志位
for(i=0;i<G->n;i++){
visited[i]=0; //初始化为0;
}
DFS(G,0); //进行深度优先遍历
for(i=0;i<G->n;i++){
if(visited[i]==0){ //若是顶点没有被访问到,则说明不连通
flag=flase;
break;
}
}
return flag;
}
int main(){
system("pause");
return 0;
}