广度优先搜索
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 255532typedef 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;elsereturn 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;elsereturn 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)之间是否有路径。```cint 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;elsereturn 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;elsereturn 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为走过的路径长度,初值为-1path[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];//取拓扑序列中的顶点 kp=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;}
