图的深度与广度优先遍历
先介绍一下两种图的结构
邻接矩阵:
由一个一维数组存储图中的顶点,由一个二维数组表示元素之间的关系,用行和列以及其中的值表示两个顶点之间是否有边(弧),如第2行第3列的值为1,则表示第2个元素与第3个元素之间存在边。
typedef struct AMGraph
{
int vex[ARRAYSIZE]={0};
int arc[ARRAYSIZE][ARRAYSIZE];
int vexnum=0,arcnum=0;//顶点个数、弧个数
int type=0;//1表示无向图,2表示有向图,3表示无向网,4表示有向网
}AMGraph;
邻接表:
由一个结构体表示图结构,内部有一个顶点数组,存储图的顶点;还有一个表示图类型的成员和一个记录顶点数量的成员。
其中顶点数组的每一个结点也都是一个结构体,有两个成员,一个存储该顶点的值;另一个是一条单链表,表示与该顶点连接的边(弧)。
单链表的每一个结点也都是一个结构体,有三个成员,一个存储另一个顶点的下标(单链表所在的结构体,是一个顶点,而这条单链表的每一个结点都是与这个顶点相连的弧,所以该弧则需要记录其所连接的另一个顶点是谁);还有两个成员分别是用来连接顶点的next指针和存储权值的变量。
//定义弧结点
typedef struct ArcNode
{
int adjvex=-1;//另一个顶点的下标
struct ArcNode *next=nullptr;
int weight=0;//权值
}ArcNode;
//定义顶点结点
typedef struct VexNode
{
int data=0;//顶点数据
ArcNode *AList=nullptr;//单链表头指针
}VexNode;
//定义邻接表
typedef struct ALGraph
{
VexNode vex[ARRAYSIZE];//顶点数组
int vexnum=0;//顶点数
int type=0;//图的种类:1表示无向图,2表示有向图,3表示无向网,4表示有向网
}ALGraph;
图的遍历:
图的遍历是从某一顶点出发访遍图中其余顶点,且每个顶点只能被访问一次。
为了避免同一个顶点重复访问,在遍历的过程中,必须记下每个已访问过的顶点。为此可以设置一个辅助数组,其下标对应着顶点数组的下标,当数组元素值为假时,代表该顶点还未被访问,值为真则代表已访问。
深度优先遍历:
图的深度优先遍历类似树的先根遍历。假设初始状态是图中所有顶点未曾访问,此时从某一顶点v出发,访问此顶点,然后以此访问该顶点的所有邻接顶点,直到与v相连的顶点都被访问了一次;若此时图中还有顶点未访问,则再次重复上述过程,直到图中所有顶点都被访问到为止。
领接矩阵实现:
bool visited[ARRAYSIZE];//标志数组:true代表该顶点已访问,false代表该顶点未访问。
void DFS(AMGraph &G,int v)
{
cout << G.vex[v] << endl;
visited[v]=true;//标记已访问
for(int i=0;i<G.vexnum;i++)//寻找下标为v的顶点的邻接顶点
{
if(G.arc[v][i]!=0 && G.arc[v][i]!=INT_MAX)
if(visited[i]==false) DFS(G,i);//如果该邻接顶点尚未访问,对其调用DFS
}
//判断是否有向
if(G.type==2 || G.type==4)
{
for(int i=0;i<G.vexnum;i++)
{
if(G.arc[i][v]!=0 && G.arc[v][i]!=INT_MAX)
if(visited[i]==false) DFS(G,i);
}
}
}
void DFSTraverse(AMGraph &G)
{
//初始化标志数组
for(int i=0;i<G.vexnum;i++) visited[i]=false;
//因为图中可能存在互不相连的子图,所以需要迭代判断每一个顶点是否已访问
for(int i=0;i<G.vexnum;i++)
if(visited[i]==false)
DFS(G,i);//对尚未访问到的结点调用DFS
}
邻接表实现:
bool visited[ARRAYSIZE];//标志数组
void DFS(ALGraph &G,int v)
{
cout << G.vex[v].data << endl;
visited[v]=true;
//访问该顶点的邻接顶点
ArcNode *p=G.vex[v].AList;
while(p!=nullptr)
{
if(visited[p->adjvex]==false)
DFS(G,p->adjvex);
p=p->next;
}
//若是有向图,搜索入度的邻接顶点
if(G.type==2 || G.type==4)
for(int i=0;i<G.vexnum;i++)
{
if(i==v) continue;
p=G.vex[i].AList;
while(p!=nullptr)
{
if(p->adjvex==v)
{
if(visited[i]==false)
DFS(G,i);
break;
}
p=p->next;
}
}
}
void DFSTraverse(ALGraph &G)
{
//初始化标志数组
for(int i=0;i<G.vexnum;i++) visited[i]=false;
//检测每一个顶点是否已访问
for(int i=0;i<G.vexnum;i++)
if(visited[i]==false) DFS(G,i);
}
广度优先遍历:
广度优先遍历类似树的层序遍历。先从图中某一点v出发,访问v,然后依次访问与v相连的顶点,接着再依次访问这些顶点的邻接顶点,以此类推,直到图中的顶点都被访问到为止。
和图的深度优先遍历一样,需要借助辅助数组记录顶点是否已访问;和树的层次遍历一样,需要借助队列结构,访问一个顶点并加入队列,顶点出队时将与之相连的顶点也加入队列。
领接矩阵实现:
void BFSTraverse(AMGraph &G)
{
queue<int> Q;//辅助队列
int pos=-1;//用于获取队头元素
//初始化标志数组
for(int i=0;i<G.vexnum;i++) visited[i]=false;
for(int i=0;i<G.vexnum;i++)
{
if(visited[i]==false)
{
cout << G.vex[i] << endl;
visited[i]=true;//标记已访问
Q.push(i);//顶点下标入队
while(!Q.empty())
{
pos=Q.front();
//访问队头顶点的邻接顶点
for(int j=0;j<G.vexnum;j++)
if(G.arc[pos][j]!=0 && G.arc[pos][j]!=INT_MAX)
{
if(visited[j]==false)
{
cout << G.vex[j] << endl;
visited[j]=true;
Q.push(j);
}
}
//判断是否有向
if(G.type==2 || G.type==4)
{
for(int j=0;j<G.vexnum;j++)
if(G.arc[j][pos]!=0 && G.arc[j][pos]!=INT_MAX)
if(visited[j]==false)
{
cout << G.vex[j] << endl;
visited[j]=true;
Q.push(j);
}
}
//队头出队
Q.pop();
}//while
}//if
}//for
}
邻接表实现:
void BFSTraverse(ALGraph &G)
{
queue<int> Q;//辅助队列
int pos=-1;//负责获取队头
ArcNode *p=nullptr;//搜索单链表的指针
//初始化标记数组
for(int i=0;i<G.vexnum;i++) visited[i]=false;
//检测每一个顶点是否已访问
for(int i=0;i<G.vexnum;i++)
if(visited[i]==false)
{
cout << G.vex[i].data << endl;
visited[i]=true;//标记已访问
Q.push(i);//入队
while(!Q.empty())
{
pos=Q.front();
//访问队头的邻接顶点
p=G.vex[pos].AList;
while(p!=nullptr)
{
if(visited[p->adjvex]==false)
{
cout << G.vex[p->adjvex].data << endl;
visited[p->adjvex]=true;
Q.push(p->adjvex);
}
p=p->next;
}
if(G.type==2 || G.type==4)
for(int j=0;j<G.vexnum;j++)
{
if(j==pos) continue;
p=G.vex[j].AList;
while(p!=nullptr)
{
if(p->adjvex==pos)
{
if(visited[j]==false)
{
cout << G.vex[j].data << endl;
visited[j]=true;
Q.push(j);
}
break;
}
p=p->next;
}
}
//队头出队
Q.pop();
}//while
}//if
}