数据结构与算法
图的遍历:
1.深度优先搜索:
深度优先遍历v0所在的连通子图
void DepthFirstSearch(Graph g,int v0)
{
visit(v0);
visited[v0]=True;
w=FirstAdjVertex(g,v0);
//如果这个邻接点存在
while(w!=-1)
{
//如果这个邻接点没有被访问过
if(!visited[w]) DepthFirstSearch(g,w);
w=NextAdjVertex(g,v0,w);//取v0的下一个顶点做DFS
}
}
深度优先遍历图g的算法描述
#define True 1
#define False 0
int visited[MAX_VERTEX_NUM];
void TraverseGraph(Graph g)
{
for(v=0;v<g.vexnum;v++)
visited[v]=False;
for(v=0;v<g.vexnum;v++)
if(!visited[v]) DepthFirstSearch(g,v);
}
深度优先搜索可以帮我们判断这个图是否为连通图,连通分量的数目以及点到点的简单路径。
采用邻接矩阵的DepthFirstSearch
void DepthFirstSearch(AdjMaxtri g,int v0)
{
visit(v0);
visited[v0]=True;
for(vj=0;vj<g.vexnum;vj++)
if(!visited[vj]&&g.arcs[v0][vj].adj==1)
DepthFirstSearch(g,vj);
}
2.广度优先搜索算法:
类似于树的层次遍历
void BreadthFirstSearch(Graph g,int v0)
{
visit(v0);
visited[v0]=True;
InitQueue(&Q);
EnterQueue(&Q,v0);
while(!Empty(Q))
{
DeleteQueue(&Q,&v);
w=FirstAdj(g,v);
while(w!=-1)
{
if(!visited(w))
{
visit(w);
visited[w]=True;
EnterQueue(&Q,w);
}
w=NextAdj(g,v,w);
}
}
}
图的应用——最小生成树
连通图:在无向图里面,任意两点之间都有路径可以相通。
完全图:在无向图里面,任意两个顶点之间有直达的边相连。
生成树:一个连通图的生成树是指一个极小连通子图,含有图中的全部n个顶点,但只有足以构成一棵树的n-1条边。
构造网的一棵最小生成树,即:在e条带权的边中选取n-1条边(不构成回路),使“权值之和”为最小。
最小生成树算法:
1.Prim普里姆算法
取图中任意一个顶点v作为生成树的根,之后往生成树上添加新的顶点w,在添加的顶点w和已经在生成树上的顶点v之间必定存在一条边,该边的权值在所有连通顶点v和w之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有n个顶点为止。
一般情况下所添加的顶点应满足条件:
在生成树的构造过程中,图中n个顶点分属两个集合:
已经落在生成树上的顶点集U和尚未落在生成树上的顶点集V-U,则应在所有连通U中顶点和v-u中顶点的边中选取权值最小的边。
struct
{
vertexData adjvex;
int lowcost;
}closedge[MAX_VERTEX_NUM]
MiniSpanTree_Prim(AdjMatrix gn,VertexData u)
{
k=LocateVertex(gn,u);
closedge[k].lowcost=0;
for(i=0;i<gn.vexnum;i++)
if(i!=k)
{
closedge[i].adjvex=u;
closedge[i].lowcost=gn.arcs[k][i].adj;
}
for(e=1;e<=gn.vexnum-1;e++)
{
k0=Minium(closedge);
u0=closedge[k0].adjvex;
v0=gn.vexs[k0]
printf(u0,v0);
closedge[k0].lowcost=0;
for(i=0;i<vexnum;i++)
if(gn.arcs[k0][i].adj<closedge[i].lowcost)
{
closedge[i].lowcost=gn.arcs[k0][i].adj;
closedge[i].adjvex=v0;
}
}
}
时间复杂度O(n的平方)
与图的顶点数相关
Kruskal克里斯卡尔算法
为使生成树上边的权值之和达到最小,则应使生成树中每一条边的权值尽可能地小
先构造一个只含n个顶点的子图SG,然后从权值最小的边开始,若它的添加不使SG中产生回路,则在SG上加上这条边,如此重复,直至加上n-1条边为止。
1.所有的边按权值从小到大排序(排序算法:堆排序)
2.顶点集合状态,边如果加入则顶点加入。(判断回路)
3.最小生成树边的集合
- 图的生成树不唯一,从不同的顶点出发进行遍历,可以得到不同的生成树;
- 既是从相同的顶点出发,在选择最小边时,可能有多条同样的边可选,此时任选其一。
Prim算法适应于稠密图(顶点多)
3.图的拓扑排序
程序的数据流图,每个顶点代表一个活动,弧
//各顶点入度的函数
void FindID(AdjList G,int indegree[MAX_VERTEX_NUM])
{
int i;
ArcNode *p;
for(i=0;i<G.vexnum;i++)
indegree[i]=0;
for(i=0;i<G.vexnum;i++)
{
p=G.vertexes[i].firstarc;
while(p!=NULL)
{
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
//将入度为0的结点入队
//整个算法实现过程
int TopoSort(AdjList G)
{
Queue Q;/*队列*/
int indegree[MAX_VERTEX_NUM];
int i,count,k;
ArcNode *p;
FindID(G,indegree);
InitStack(&S);
for(i=0;i<G.vexnum;i++)
if(indegree[i]==0)
EnterQueue(&Q,i);
count=0;
while(!StackEmpty(S))//判断队列是否为空
{
DeleteQueue(&Q,&i);
printf("%c",G.vertex[i].data);
count++;
p=G.vertexes[i].firstarc;
while(p!=NULL)
{
k=p->adjvex;
indegree[k]--;
if(indegree[k]==0)
EnterQueue(&S,k);
p=p->nextarc;
}
}
if(count<G.vexnum)
return(Error);
else return(Ok);
}
4.图的关键路径
在有向网中,如果用顶点表示事件,用有向边表示活动,边上的权值表示活动持续的时间,则称这样的有向网为弧表示活动的网(AOE-网)。对仅有一个开始点和一个完成点的工程,可用AOE-网来表示,它可用来估算工程完成的时间。
AOE网的特征:
1.只有在某顶点所代表的事件发生后,从该顶点发出的所有有向边所代表的活动才能开始;
2.只有在进入某一顶点的各有向边所代表的活动均已完成,该顶点所代表的事件才能发生。
关键路径:
从有向图的源点到汇点的最长路径,即关键路径。
关键活动持续时间的总和(关键路径的长度)
ve(j):事件(顶点)的最早发生时间
vl(k):从顶点k到汇点的最短路径长度,即事件(顶点)的最迟发生时间
关键路径:ee(i)=ve(j)
el(i)=vl(k)-dut(
//按拓扑顺序求出每个事件的最早发生时间
//按逆拓扑序列求出每个事件的最迟发生时间
//计算每个活动的最早开始时间和最晚发生时间
//找出关键活动,即ee(a_i)=el(a_i)
//改造拓扑排序算法
int TopoSort_Ve(AdjList G,Stack *T)
{
Queue Q;
int indegree[MAXVEX],ve[MAXVEX];
int i,count=0,k;
ArcNode *p;
FindID(G,indegree);
InitQueue(&Q);InitStack(&T);
for(i=0;i<G.vexnum;i++)
if(indegree[i]==0)
EnterQueue(&Q,i);
for(i=0;i<G.vexnum;i++)
ve[i]=0;
while(!IsEmpty(Q))
{
DeleteQueue(&Q,&i);Push(T,i);count++;
p=G.vertex[i].head;
while(p!=NULL)
{
k=p->adjvex;
if(--indegree[k]==0)
EnterQueue(&Q,k);
if(ve[j]+p->weight>ve[k])
ve[k]=ve[j]+p->weight;
p=p->next;
}
}
if(count<G.vexnum) return 0;
else return 1;
}
Int CriticalPath(AdjList G)
{
ArcNode *p;
int i,j,k,a,eei,eli,flag=0;
int vl[MAXVEX];
Stack S;
if(!TopoSort_Ve(G,&T))
return(0);
for(i=0;i<G.vexnum;i++)
vl[i]=ve[G.vexnum-1];
while(!IsEmpty(&T))
{
pop(T,&j);
p=G.vertex[j].head;
while(p)
{
k=p->adjvex;
a=p->weight;
if(vl[k]-a<vl[j])
vl[j]=vl[k]-a;
p=p->next;
}
}
for(i=0;i<vexnum;i++)
{
p=G.vertex[i].head;
while(p)
{
k=p->adjvex;
a=p->weight;
eei=ve[j];
eli=vl[k]-a;
if(eei==eli) flag=1;//活动的最早开始时间和最晚发生时间
p=p->next;
}
}
}
时间复杂度?
5.图的单源最短路径
1.从单源点到其余各点的最短路径:Dijkstra
依最短路径的长度递增的次序求得各条路径。其中,从源点v0到顶点vi的最短路径是v0到各点最短路径集合中长度最短者。
2.每一对顶点之间的最短路径:Floyd
贪心算法的内容后续更新~