广度优先搜索
bool visited[max_vertex_num];
void BFSTrave(Graph G){
for(int i=0;i<G.vexnum;++i){
visited[i]=false;
}
Queue Q;
InitQueue(Q);
for(i=0;i<G.vexnum;i++){
if(!visited[i])
BFS(G,i);
}
}
void BFS(Graph G,int v0){
visit(v0);
visited[v0]=true;
EnQueue(Q,v0);
while(isEmpty(Q)){
DeQueue(Q,v);
for(int w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
if(!visited[w]){
visit[w];
visited[w]=true;
EnQueue(Q,w);
}
}
}
}
深度优先搜索
bool visited[max_vertex_num];
void DFSTrave(Graph G){
for(int i=0;i<G,vexnum;i++)
visited[i]=false;
for(int i=0;i<G.vexnum;i++){
if(!visited[i])
DFS(G,i);
}
}
void DFS(Graph G,int v0){
visit(v0);
visited[v0]=true;
for(w=FirstNeighbor(G,v0);w>=0;w=NextNeighbor(G,v0,w)){
if(!visited[w])
visit(w);
DFS(G,w);
}
}
//借助栈
void DFS(Graph G,int v0){
Stack S;
InitStack(S);
for(int i=0;i<G.vexnum;i++)
visited[i]=false;
Push(S,v0);
visited[v0]=true;//用visited数组表示顶点是否在栈内
while(isEmpty(S)){
int k=Pop(S);
visit(k);
for(int w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
if(!visited[w]){
Push(S,w);
visited[w]=true;
}//if
}//for
}//while
}
普利姆算法(选取权值最小边加入,直到所有顶点加入)
执行过程:1.将v0到其它顶点的所有边当作候选边
2.重复下面步骤n-1次,使得其它n-1个顶点被并入到生成树中
①从侯选边中挑选出权值最小的边输出,并将与该边另一端相接的顶点并入生成树
②考察所有剩余顶点vi,如果(v,vi)的权值比lowcost[vi]小,则用(v,vi)的权值更新lowcost[vi]
#definr int INF 255532
typedef struct{
int edges[maxsize][maxsize];
int n,e;//顶点数,边数
VertexType vex[maxtype];//存放结点信息
}MGraph;
void Prim(MGraph G,int v0,int &sum){
int lowcost[maxsize],vset[maxsize],v;
int i,j,k,min;
v=v0;
for(i=0;i<G.n;++i){
lowcost[i]=G.edges[v0][i];//初始化lowcost数组
vset[i]=0;//初始化结点状态数组
}
vset[0]=1;//v0并入树中
sum=0;//累计树的权值
for(i=0;i<G.n-1;++i){
min=INF;
//下面这个循环用于选出侯选边中的最小者
for(j=0;j<G.n;++j){
if(vset[j]==0&&lowcost[j]<min){
min=lowcost[j];
k=j;//k记录要并入结点序号
}
}
vset[k]=1;
v=k;
sum+=min;
//下面这个循环以刚并入的顶点v为媒介更新候选边
for(j=0;j<G.n;++j)
if(vset[j]==0&&G.edges[v][j]<lowcost[j])
lowcost[j]=G.edges[v][j];//更新lowcost数组
}
}
克鲁斯卡尔算法(每次找出候选边中权值最小的边并入生成树,直到所有边都被检测完)
typedef struct{
int a,b;//a和b是一条边所连的两个顶点
int w;//边的权值
}Road;
Road road[maxsize];
int v[maxsize]//定义并查集数组
int getRoot(int a){//在并查集中查找根结点的函数
while(a!=v[a])//v[a]存放的是a的根
a=v[a];
return a;
}
//快排
int Partition(Road road[],int low,int high){
int pivot=road[low].w;
while(low<high){
while(low<high&&road[high].w>=pivot)
--high;
A[low].w=A[high].w;
while(low<high&&road[low]<=pivot)
++low;
A[high].w=A[low].w;
}
A[low].w=pivot;
return low;
}
void QuickSort(Road road[],int low,int high){
if(low<high){
int pivotpos=Partition(road,low,high);
QuickSort(road,low,pivotpos-1);
QuickSore(road,pivotpos+1,high);
}
}
void Krusal(MGraph G,int &sum,Road road[]){
int i;
int N,E,a,b;
N=G.n;//顶点数
E=G.e;//边数
sum=0;
for(i=0;i<N;++i)
v[i]=i;
QuickSort(road,0,E-1);//对road数组中的E条边按其权值从小到大排
for(i=0;i<E;++i){
a=getRoot(road[i].a);//获取边的两个端点的根
b=getRoot(road[i].b);
if(a!=b){
v[a]=b;
sum+=road[i].w;
}
}
}
弗洛伊德算法(任意一对顶点间的最短路径)
矩阵A记录当前已经求得任意两个顶点最短路径的长度,Path用来记录当前两顶点间最短 路径上要经过的中间顶点。
void Floyd(MGraph *G,int Path[][maxsize],int A[][maxsize]){
int i,j,k;
//对两个矩阵初始化
for(i=0;i<G->n;++i){
for(j=0;j<G->n;++j){
A[i][j]=G->edges[i][j];
Path[i][j]=-1;
}
}
//下面的循环实现以k为中心点对所有顶点对(i,j)进行检测和修改
for(k=0;k<G->n;++k)
for(i=0;i<G->n;++i)
for(j=0;j<G->n;++j)
if(A[i][j]>A[i][k]+A[k][j]){
A[i][j]=A[i][k]+A[k][j];
Path[i][j]=k;
}
}
void printPath(int u,int v,int Path[][maxsize],int A[][maxsize]){
if(A[u][v]==INF)
printf("无最短路径!");
else{
if(Path[u][v]==-1)
printf("u->v");
else{
int mid=path[u][v];
printPath(u,mid,Path,A);//处理mid前半段路径
printPath(mid,v,Path,A);//处理mid后半段路径
}
}
}
拓扑排序
在一个有向图中找到一个拓扑排序序列的过程如下:
1)从有向图中选择一个没有前驱(入度为0)的顶点输出;
2)删除1)中的顶点,并且删除从该顶点发出的全部边
3)重复上述两步,直到剩余的图中不存在没有前驱的顶点为止
typedef struct{
char data;
int count;
ArcNode *firstarc;
}VNode;
int TopSort(AGraph *G){
int i,j,n=0;
int stack[maxsize];//定义并初始化栈
int top=-1;
ArcNode *p;
//这个循环将图中入度为0的顶点入栈
for(i=0;i<G->n;++i){
if(G->adjList[i].count==0)
stack[++top]=i;
while(top!=-1){
i=stack[top--];//顶点出栈
++n;//计数器加一,统计当前顶点
printf("%d",i);//输出当前顶点
p=G->adjList[i].firstarc;
//这个循环实现将所有顶点引出的边所指向的顶点的入度-1,并将在此之后入度变0的顶点入栈
while(p!=NULL){
j=p->adjvex;
--(G->adjList[j].count);
if(G->adjList[i].count==0)
stack[++top]=j;
p=p->nextarc;
}
}
if(n==G->n)
return 1;
else
return 0;
}
1.设计一个算法,判断一个无向图 G 是否为一棵树。若是一棵树,则算法返回 true,否则返回 false.
一个无向图G是一棵树的条件,G 必须是无回路的连通图或有n-1 条边的连通图。 这里采用后者作为判断条件。采用深度优先遍历算法在遍历图的过程中统计可能访问到的顶点个数和边的个数,若一次遍历就能访问到 n 个顶点和 n-1 条边,则可断定,此图是一棵树,算法如下:
bool isTree(Graph &G){
for(int i=0;i<G.vexnum;i++)
visited[i]=false;
int Vnum=0,Enum=0;
DFS(G,1,Vnum,Enum,visited);
if(Vnum==G.vexnum&&Enum=2*(G.vexnum-1))
return true;
else
return false;
}
void DFS(Graph &G,int v,int &Vnum,int &Enum,int visited[]){
//深度优先遍历
visited[v]=true;
Vnum++;
int w=FirstNeighbor(G,v);
while(w!=-1){
Enum++;
if(!visited[w])
DFS(G,w,Vnum,Enum,visited);
w=NextNeighbor(G,v,w);
}
}
- 分别采用基于深度优先遍历和广度优先遍历算法判别以邻接表方式存储的有向图中是否存在有顶点 Vi到顶点 Vj的路径(i≠j)。
3.设计一个算法,求不带权无向连通图 G 中距离顶点 v 最远的一个顶点(所谓最远就是到大 v 的路径长度最大)bool visited[maxsize]=false;
int Exit_Path_DFS(ALGraph G,int i,int j){
int w;//顶点序号
if(i==j)
return 1;
else{
visited[i]=true;
for(w=FirstNeighbor(G,i);w>=0;w=NextNeighbor(G,i,w)){
if(!visited[w]&&Exit_Path_DFS(G,w,j))
return 1;
}
}
return 0;
}
基于图的广度优先搜索遍历方式,其体现了图中由某个顶点开始,以近向远扩展的方式遍历图中结点的过程。因此广度优先搜索遍历过程的最后一个顶点一定是距离给定顶点最远的顶点。 ```c int Maxdist(ALGraph G,int v){ ArcNode p; int Q[maxvexnum],front=rear=0; int visited[maxvexnum],i,j,k; for(i=0;ivexnum;i++)
Q[++rear]=v; visited[v]=1; while(front!=rear){visited[i]=0;
} return k; }k=Q[++front];
p=G->adjlist[k].firstarc;
while(p!=NULL){
j=p->adjvex;
if(visited[j]==0){
visited[j]=1;
Q[++rear]=j;
}
p=p->nextarc;
}
4.假设图采用邻接表存储,分别写出基于 DFS 和 BPS 遍历的算法来判别顶点 i和顶点 j (i≠j)之间是否有路径。
```c
int DFSTrave(ALGraph *G,int i,int j){
int k;
for(k=0;k<G->vexnum;k++)
visited[k]=false;
DFS(G,i);
if(visited[j]==false)
return 0;
else
return 1;
}
int BFSTrave(ALGraph *G,int i,int j){
int k;
for(k=0;k<G.vexnum;k++)
visited[k]=false;
BFS(G,i);
if(visited[j]==false)
return 0;
else
return true;
}
void DFS(ALGraph G,int v0){
Stack S;
InitStack(S);
Push(S,v0);
visited[v0]=true;
while(isEmpty(S)){
int k=Pop(S);
visit(k);
for(int w=FirstNeighbor(G,v0);w>=0;w=NextNeighbor(G,v0,w)){
if(!visited[w]){
Push(S,w);
visited[w]=true;
}
}
}
}
5.假设图 G 采用邻接表存储,设计一个算法,判断无向图 G 是否连通。若连通则返回 1;否则返回 0。
int Connect(ALGraph *G){
int flag=1;
for(int i=0;i<G->vexnum;i++)
visited[i]=false;
DFS(G,0);//BFS(G,0);
for(i=0;i<G->vexnum;i++){
if(visited[i]==false){
flag=0;
break;
}
}
return flag;
}
6.假设图 G 采用邻接表存储,设计一个算法,判断图 G 中从顶点 u 到 v 是否存在简单路径。
void ExitPath(ALGraph *G,int u,int v,int &has){
int w;
ArcNode *p;
visited[u]=1;
if(u==v){
has=1;
return;
}
p=G->adjlist[u].firstarc;
while(p!=NULL){
w=p->adjvex;
if(visited[w]==0)
ExitPath(G,w,v,has);
p=p->nextarc;
}
}
7.假设图G 采用邻接表存储,设计一个算法,输出图G 中从顶点 u 到 v 的所有简单路径
void FindPath(ALGraph *G,int u,int v,int path[],int d){
int w,i;
ArcNode *p;
d++;//d为走过的路径长度,初值为-1
path[d]=u;
visited[u]=1;
if(u==v){
for(int i=0;i<d;i++)
printf("%2d",path[i]);//输出路径
}
p=G->adjlist[u].firstarc;
while(p!=NULL){
w=p->adjvex;
if(!visited[w])
FindPath(G,w,v,path,d);
p=p->nextarc;
}
visited[u]=0;
}
8.假设图 G 采用邻接表存储,设计一个算法,判断无向图 G 中任意两点之间是否存在一条长度为 k 的简单路径
int visitd[maxsize];
int PathLenk(ALGraph *G,int i,int j,int k){
if(i==j&&k==0)
return 1;
else if(k>0){
visited[i]=true;
ArcNode *p;
for(p=G.adjlist[i].firstarc;p;p=p->nextarc){
int w=p->adjvex;
if(!visited[w]){
if(PathLenk(G,v,j,k-1))
return 1;
}
}
visited[i]=false;//允许曾经访问过的结点出现在另外一条路径上
}
return 0;
}
9.求AOE网的所有关键活动
int CriticalPath(AGraph *G){
int i,j,k;
int ve[maxv],vl[maxv];//事件的最早发生时间和最迟发生时间
int e[maxe],l[emaxe];//活动的最早发生时间和最迟发生时间
int d[maxe];//活动的时间余量
int n=0;//活动的下标
int Topsq[maxv];//保存拓扑序列
ArcNode *p;
if(TopSort(G.Topsq)==0)//拓扑排序不成功
return 0;
printf("拓扑序列:");
for(i=0;i<G->n;i++)
printf("%d",Topsq[i]);
printf("\n");
for(i=0;i<G->n;i++)
ve[i]=0;//ve[]置初值
for(i=0;i<G->n;i++){//按拓扑序列求每个事件的最早发生时间
k=Topsq[i];//取拓扑序列中的顶点 k
p=G->adjList[k].firstarc;
while(p!=NULL){//修改顶点k的所有后继顶点的最早发生时间
j=p->adjvex;//求顶点的后继顶点
if(ve[j]<ve[k]+p->weight)
ve[j]=ve[k]+p->weiht;
p=p->nexarc;
}
}
printf("各事件的最早发生时间:\n");
for(i=0;i<G->n;i++){
printf("ve[%-2d]=%d\t",i,ve[i]);
if((i+1)%3==0)
printf("\n");
}
printf("\n");
for(i=0;i<G->n;i++){//给每个事件最迟发生时间置初值
vl[i]=ve[G->n-1];
for(i=G->n-1;i>=0;i--){
k=Topsq[i];
p=G->adjList[k].firstarc;
while(p!=NULL){//修改顶点k所有后继顶点的最迟发生时间
j=p->adjvex;
if(vl[k]>vl[j]-p->weight)
vl[k]=vl[j]-p->weight;
p=p->nextarc;
}
}
printf("各事件的最迟发生时间:\n");
for(i=G->n-1;i>=0;i--){
printf("vl[%-2d]=%d",i,vl[i]);
if((i+1)%3==0)
printf("\n");
}
printf("\nAOE网的关键活动为\n");
for(i=0;i<G->n;i++){
p=G->adjList[i].firstarc;
while(p!=NULL){
j=p->adjvex;
e[n]=ve[i];
l[n]=vl[i];
d[n]=e[n]-l[n];
if(d[n]==0)//输出关键活动
printf("%d,%d",i,j);
n++;
p=p->nextarc;
}
}
printf("\n");
return 1;
}