广度优先遍历、深度优先遍历、拓扑序列算法实现
一、广度优先遍历
邻接矩阵存储结构
typedef char VerTexType;
typedef int ArcType;
/* 邻接矩阵表示法 */
typedef struct Graph {
char vertex[vexnum]; //顶点表
EleType arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /* 邻接矩阵 */
int vexnum, arcnum; /* 图的顶点数和弧数 */
} AMraph ,Graph;
要点:
- 找到与一个顶点相邻的结点
- 标记数组(标记哪些顶点被访问)
- 需要一个辅助队列
- 图的基本操作
(1)FIrstAdjVex(G,v):求图G中顶点的第一个相邻结点,若存在,返回顶点号,若v不存在邻接点或图中不存在x,则返回-1
(2)NextAdjvex(G,v,w):除v外的,下一个邻接点W,若存在,返回顶点号,若v不存在邻接点或者图中不存在x,则返回-1
//寻找图G中顶点V的第一个邻接点,若存在,返回改结点的列数;不存在,返回-1
int FirstAdjVex(Graph G,int v){
int i=0;
fot(int i=0;i<G.vexnum;i++){
if(G.arcs[v][i]==1){
return i;
}
}
return -1;
}
//寻找图G中开始结点v除了w之外的邻接结点
int NextAdjVex(Graph G,int v,int w){
int i;
for(i=w+1;i<G.arcs[v][w];i++){ //跳过w进行遍历,若存在,返回该结点的列数;不存在,返回-1
if(G.arcs[v][w]==1)
return i;
}
return -1;
}
// 广度优先遍历---BFS
void BFS(Graph G,int v){//v是开始结点
InitQueue(Q); //初始化一个队列
int w,u;
for(int i=0;i<G.vexnum;i++)//初始化vextex数组(访问数组)
G.visited[i]=false;
printf("%d ",v);//访问v
visited[v]=ture;//已访问过置为true
EnQueue(Q,v);//将结点入队
while(!QueueEmpty(Q)){
DeQueue(Q,u); //将对头元素出队并置为u
for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,u,w)){
if(!Visited[w]){ //当前结点为访问的时候
printf("%d ",w);visited[w]=ture;//访问w,并标记
EnQueue(Q,w); //w 入队
}
}
}
}
二、深度优先遍历(递归)
算法思想:
- 从图中一个顶点V出发,访问V,并置visited[v]=true
- 依次检查V的所有临结点w,如果visited【w】==false,再从w出发遍历,知道图中的所有结点都已经访问过
bool visited[MVNum]; //访问标志数组,初值全为false
void DFS(ALGraph G,int v){
//从第v个结点出发,深度优先遍历图G
printf("%d ",v);visited[v]=ture;//访问并标记
for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w)){
//依次检查v的所有邻接点w,FirstAdjVex(G,v)表示V的第一个邻接点
//NextAdjVex(G,v,w):表示v的邻接点除了w之外的邻接点
if(!visited[w]) DFS(G,w); //对为访问的结点w,直接递归调用DFS(G,w);
}
}
三、拓扑排序:判断有向图是否存在回路
图的邻接表的存储结构
#define MVNum 100 //最大顶点数
typedef struct ArcNode{ //边结点
int adjvex; //该边所指向的结点的位置
struct ArcNode *nextarc; //指向下一条边的指针
int info; //和边有关的信息---权值
}ArcNode;
typedef struct VNode{ //顶点信息
VerTexType data;
ArcNode *firstarc; //指向第一条依附改顶点的边的指针
}VNode,AdjList[MVNum]; //AdjList表示邻接表的类型
typedef struct{
AdjList vertices;
int vexnum,arcnum; //图的当前顶点数和边数
}ALGraph;
算法思想:
- 求出各顶点的入度存入到indegree[i],并将入度为0的点入栈
- 若栈不为空
A:将栈顶的顶点Vi出栈并保存在拓扑序列数组topo中
B:对顶点Vi的每个邻接点Vk的入度减一,如果Vk的入度为0,则将Vk入栈 - 如果输出的顶点数小于AOV网的顶点个数,则网中存在有向环,无法进行拓扑排序,不然,拓扑序列成功
//拓扑排序:判断有向图是否有没有回路 向图G采用邻接表的存储结构
bool ToPuSort(ALGraph G,int topo[]){
//若G没有回路,生成G的拓扑序列并存入到topo[]
FindInDegree(G,indegree); //FIndIndegree()函数胡:求各个结点的入度并存放到indegree[]数组中。
InitStack(S); //初始化一个工作栈
for(int i=0;i<G.vexnum;i++)
if(!indegree[i]) push(S,i); //入度为0的入栈
count=0; //记数--用与和顶点数比较
while(!StackEmpty(S)){ //栈不为空
pop(S,i); //弹出栈顶元素,并存到拓扑序列数组topo中
topo[count]=i;
count++;
for(p=G.vertices[i].firstarc;p;p=p->nextarc){
k=p->adjvex; //Vk为Vi的邻接点
--indegree[k]; //Vi的每个邻接点入度减一
if(indegree[k]==) push(S,k);
}
}
//计数器count和vexnum比较
if(count<vexnum) return false; // 该图有回路?
else return ture;
}