图
图的定义
图(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