图(1)

图的定义

图(graph)是由有限个顶点(vertex)和有限条边(edge)所组成的集合.
通常用G={V,E}来表示图,V为顶点集合,E为边集合.
图有两种:无向图和有向图.

基本术语

在含n个顶点的无向图中,如果正好有 n(n-1)/2 条边.则称此图为完全无向图.
在含n个顶点的有向图中,如果正好有 n(n-1)/2 条边.则称此图为完全有向图.
完全无向图或完全有向图,简称完全图.
两个不同顶点之间所经过的边称为路径.
起始顶点与终止顶点为同一个顶点的路径称为回路.
在无向图中,两个顶点有路径,则称这两个顶点连通.
若图中任意两顶点均连通,则称该图为连通图.
路径上所包含边的总数称为路径长度.
两顶点间的路径,称为两个顶点的一个连通分支.分支不唯一
在有向图中,若任意两顶点间有两条相反的边,则称为此图为强连通的.
在无向图中,一个顶点所拥有边数称为该顶点的度.
在有向图中,指向顶点V且带有箭头的边称为顶点V的入度,反之,为出度,

图的ADT

ADT Graph
数据:
有限个顶点和有限条边所组成的集合.
运算:
	Create()		创建图
	Display()		输出图
	Traverse()		图的顶点遍历
	MST()		求最小代价树
	MPL()		求最短路径长度

邻接矩阵的表示法

对于含n(n>1)个顶点的图G={V,E},
用一维数组V[n]存储顶点信息,
用二维数组M[n][n]存储边信息.
#include<iostream>
using namespace std;
#define MaxSize 20
//图定义 
struct Graph{
	int V[MaxSize];          //存储顶点的数组 
	int M[MaxSize][MaxSize]; //存储边的数组 
	int n;					 //图所含顶点个数 
}; 
//根据顶点集V 边集合E 创建图的领接矩阵 
void Create(Graph &G,int *V,int vn,int *E,int en,int action){
	int i,j,k,w;
	G.n=vn;
	for(i=0;i<G.n;i++)
		G.V[i]=V[i];
	for(i=0;i<G.n;i++){
		for(j=0;j<G.n;j++)
				G.M[i][j]=0;
	}
	for(k=0;k<en;k++){
		i=E[3*k+0];
		j=E[3*k+1];
		G.M[i][j]=1
		if(action==0) //如果是无向图 
			G.M[j][i]=1;
	}	
}
//基于领接矩阵的广度优先遍历
void BFS(Graph &G,int sv){
	int visited[MaxSize];  //顶点访问标识数组 
	int queue[MaxSize];    //顶点号队列
	int i,j,front,rear;
	for(i=0;i<G.n;i++)
		visited[i]=0;
	front=rear=-1;
	queue[++rear]=sv;
	visited[sv]=1;
	while(front<rear){
		i=queue[++front];
		cout<<i<<" ";
		for(j=0;j<G.n;j++){
			if(G.M[i][j]==1){
				if(visited[j]==0){
					queue[++rear]=j;
					visited[j]=1;
				}				
			}
		}
	}	 
	cout<<endl;
}  
//输出领接矩阵
void Display(Graph G){
	int i,j;
	cout<<"顶点"<<"\t";
	for(i=0;i<G.n;i++)
		cout<<"V"<<G.V[i]<<"\t";
	cout<<endl;
	for(i=0;i<G.n;i++){
		cout<<"V"<<G.V[i]<<"\t";
		for(j=0;j<G.n;j++)
			cout<<G.M[i][j]<<"\t";
		cout<<endl;	
	}
} 
int main(){
	int V[5]={0,1,2,3,4};
	int E[7][2]={{0,1},{0,4},{1,2},{1,3},{2,3},{2,4},{3,4}};
	Graph G;
	Create(G,&V[0],5,&E[0][0],7,0);
	cout<<"图的领接矩阵"<<endl;
	Display(G); 	
}
用邻接矩阵表示图,空间复杂度为O(n^2),且邻接矩阵含较多的0元素,这会造成存储空间的浪费;
利用邻接矩阵,计算顶点的出度和入度较为方便.

邻接链表的表示法

如果将于顶点V[i]相连的所有顶点V[j]组成一条单链表,
则称这n条单链表为图的邻接链表.
#include<iostream>
using namespace std;
#define MaxSize 20
//结点定义
struct Node{
	int num;//顶点编号
	Node *next; 
}; 
//图定义
struct Graph{
	Node *V[MaxSize];//链表顶点数组
	int n;           //图所含顶点个数 
}; 
//根据顶点集V 边集合E 创建图的领接矩阵 
void Create(Graph &G,int *V,int vn,int *E,int en,int action){
	Node *NewNode,*p;
	int i,j,k;
	G.n=vn;
	//创建链表头结点数组
	for(i=0;i<G.n;i++){
		NewNode=new Node;
		NewNode->num=i;
		NewNode->next=NULL;
		G.V[i]=NewNode;
	} 
	for(k=0;k<en;k++){
		i=E[2*k+0];
		j=E[2*k+1];	
		NewNode=new Node;
		NewNode->num=j;
		NewNode->next=NULL;
		p=G.V[i];
		while(p->next!=NULL)
			p=p->next;
		p->next=NewNode;
		if(action==0){
			NewNode=new Node;
			NewNode->num=i;
			NewNode->next=NULL;
			p=G.V[j];
			while(p->next!=NULL)
				p=p->next;
			p->next=NewNode;	
		}	
	}
} 
//输出邻接链表
void Display(Graph G){
	Node *p;
	for(int i=0;i<G.n;i++){
		p=G.V[i];
		cout<<p->num<<"=>";
		while(p->next!=NULL){
			p=p->next;
			cout<<p->num<<"=>";
		}
		cout<<"NULL"<<endl;
	}
} 
//基于邻接链表的深度优先遍历
void DFS(Graph G,int sv){
	int visited[MaxSize];
	int stack[MaxSize];
	int i,j,top;
	Node *p;
	for(i=0;i<G.n;i++)
		visited[i]=0;
	top=-1;
	stack[++top]=sv;
	visited[sv]=1;
	while(top!=-1){
		i=stack[top--];
		cout<<i<<" ";
		p=G.V[i];
		while(p->next!=NULL){
			p=p->next;
			j=p->num;
			if(visited[j]==0){
				stack[++top]=j;
				visited[j]=1;
			}
		}
	}
	cout<<endl;
} 
int main(){
	int V[5]={0,1,2,3,4};
	int E[7][2]={{0,1},{0,4},{1,2},{1,3},{2,3},{2,4},{3,4}};
	Graph G;
	Create(G,&V[0],5,&E[0][0],7,0);
	cout<<"图的领接链表"<<endl;
	Display(G); 
	cout<<"深度优先遍历";
	DFS(G,0); 
} 

图的遍历

若从已给的连通图中某一顶点出发,沿着一些边访问遍图中所有的顶点,且每一顶点只被访问一次.
称为图的遍历(graph traversal).通过图的遍历,可以该图是否连通,并找出连通分支及路径

深度优先遍历

深度优先遍历(depth first search 简称DFS)是利用一个顶点访问数组和顶点栈,
以图的某个顶点为开始顶点,对图中其它顶点进行遍历的.
//基于邻接链表的深度优先遍历
void DFS(Graph G,int sv){
	int visited[MaxSize];         //顶点访问标识数组
	int stack[MaxSize];			  //顶点号栈
	int i,j,top;
	Node *p;
	for(i=0;i<G.n;i++)
		visited[i]=0;			//标识所有顶点均未被访问
	top=-1;						//置顶点栈为空
	stack[++top]=sv;			//出发顶点vs入栈
	visited[sv]=1;				//标识顶点vs已被访问
	while(top!=-1){				//当栈非空时
		i=stack[top--];			//栈顶元素出栈
		cout<<i<<" ";
		p=G.V[i];				//让探测指针p指向邻接链表的第i条链
		while(p->next!=NULL){	//当第i条链非空时
			p=p->next;			//p后移
			j=p->num;			//获得结点标号j
			if(visited[j]==0){	//结点j未被访问
				stack[++top]=j;	//顶点j入栈
				visited[j]=1;   //标记顶点j已被访问
			}
		}
	}
	cout<<endl;
} 

广度优先遍历

广度优先遍历(breadth first search 简称BFS)是利用一个顶点访问数组和顶点队列,
以图的某个顶点为开始顶点,对图中其它顶点进行遍历的.	
//基于领接矩阵的广度优先遍历
void BFS(Graph &G,int sv){
	int visited[MaxSize];  		//顶点访问标识数组 
	int queue[MaxSize];    		//顶点号队列
	int i,j,front,rear;
	for(i=0;i<G.n;i++)
		visited[i]=0;			//标识所有顶点均未被访问
	front=rear=-1;              //置顶点号队列为空
	queue[++rear]=sv;			//出发顶点vs入队
	visited[sv]=1;				//标识顶点vs已被访问
	while(front<rear){			//队列不为空时
		i=queue[++front];		//队头i出队
		cout<<i<<" ";		
		for(j=0;j<G.n;j++){		//对邻接矩阵的第i行元素一次扫描
			if(G.M[i][j]==1){	//如果顶点i和j连通
				if(visited[j]==0){	//顶点j未被访问
					queue[++rear]=j; //顶点j入队
					visited[j]=1;	//标识j已被访问
				}				
			}
		}
	}	 
	cout<<endl;
}  

写于2020-10-25

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值