数据结构之对图的学习

图(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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值