2015考研:计算机数据结构常用算法(七)?
对于无向图,e的范围是:
数据结构中讨论的图都是简单图,任何两个节点之间都不会有双边。
对于有向图,e的范围是:
图的各种存储结构
邻接矩阵方便访问任意两点的边,但不方便计算其邻接点。在深度和广度遍历中,广泛需要要求某一点的相邻点。所以邻接矩阵只在Floyed和Prim和Dijstra中使用。
邻接表可以很容易的找到一个顶点的邻接点,索引对于遍历相关的算法大多使用邻接表。例如深度、宽度、拓扑排序和关键路径。但是他也有一些缺点,就是不方便进去或者那些点可以供他操作。于是有人引入了反向邻接表。最后,人们将这两种表结合在一起,即交叉链表和相邻多表。一种是存储有向图,另一种是存储无向图。
在交叉链表和相邻多表中查找相邻点和对应的逆运算非常方便。所以在实际应用中,凡是邻接表能实现的,一定要用交叉链表和邻接多重表来实现。并且它们的存储效率更高。
1.邻接矩阵(有向图和无向图和网)也称为数组表示。
typedef结构
{ vextype vexs[maxn];∨顶点存储空间∨
adj type A[maxn][maxn];∑邻接矩阵∧
int vexnum,arcnum//图形的顶点数和边数
GraphKind类;//图形的类型
} mgraph
2.邻接表(有向图和无向图和网络)
Typedef结构节点edge
{ int adjint w;∑邻接点和权值
结构节点* next指向下一个弧或边
} linknode
Typedef结构∨顶点类型∨
{ vtype数据;∑顶点范围
linknode * farc∑指向与该顶点相关联的第一条弧或边
} Vnode
typedef结构
{
vnode G[maxn];∨顶点表∨
int vexnum,arcnum
GraphKind类;
} ALGraph
adjvexnextarcinfo
边缘节点
数据firstarc
顶点节点
3.交叉链表(有向图和有向网络)
headvextaivexhlinktlinkinfo
边缘节点
datafirstinfirstout
顶点节点
4.邻接多表(无向图)
markivexjvexilinkjlinkinfo
边缘节点
数据边缘
顶点节点
有向无环图(DAG):是用公共子表达式描述表达式的有效工具。二叉树也可以表示表达式,但是使用有向无环图可以实现同一个子公式的共享,从而节省存储空间。
顶点度数:
无向图:一个顶点V的度数为D(V),表示与V关联的边数。
有向图:顶点V的度d(V)= ID(V)+OD(V)
强连通分支:在有向图中,如果图中任意两个顶点之间有一条路,则称为强连通图。图中的最大强连通子图称为强连通分量。
这里的“Maxim”是指如果在一个连通分支上加上顶点和边,就不能形成原图中的一个连通子图,即该连通分支是一个集合最大的连通子图。有向图的连通性意味着有向图是
强连通的
遍历图的过程本质上是为每个顶点寻找其邻点的过程,其耗时主要取决于所采用的存储结构。图用邻接矩阵存储时,求每个顶点的邻接点需要时间o(),其中n是图中顶点的个数。用邻接表存储图时,求邻接点所需时间为O(e),其中e是图中边或有向弧的个数。因此,当采用邻接表作为存储结构时,首先对遍历图的时间复杂度O(n+e)进行深度搜索。
广度优先搜索遍历图和深度优先搜索遍历图的时间复杂度是一样的,两者唯一的区别是访问节点的顺序不同。也就是说,它们的时间复杂度取决于所采用的存储结构。用邻接矩阵存储时,复杂度为O(),用邻接表存储时,时间复杂度为O(n+e)。
建图算法:(邻接表常考,邻接矩阵简单,交叉链表和多重表很像建邻接表)
void creat graph(adj list & amp;G) //建立n个顶点m条边的无向图的邻接表存储结构。
{ int n,m;
scanf("%d%d ",& ampn & amp;m);//输入顶点和边的数量。
for (i =1,i & lt= n;I++)//输入顶点信息,建立顶点向量。
scanf(& amp;g[i]。顶点);
g[i]。firstarc = null
}
for(k = 1;k & lt= m;K++)//输入边信息
scanf(& amp;v1。v2);//输入两个顶点。
i= LocateVertex (g,v 1);j=GraphLocateVertex (g,v2);//顶点定位
p =(ArcNode *)malloc(sizeof(ArcNode));//申请一个边缘节点
p->;adj vex = j;
p->;next=g[i]。firstarcg[i]。first arc = p;//将边缘节点链接到边缘列表的头部。
p =(ArcNode *)malloc(sizeof(ArcNode));//因为是无向图,所以应该在另一个里面。
p->;adj vex = I;
p->;next=g[j]。firstarcg[j]。frstarc = p;//将节点插入顶点的边表中。
}
}//算法CreatGraph结束
求最小生成树的两种算法(Prim和Kruskal)
Prim算法有一个双循环,外层是寻找n-1条边,内层是寻找closedge[v]中的最小值。lowcost和当前添加点对closedge[]的影响是并行发现的。所以它的时间复杂度是O(),与途中边数无关,所以更适合边密集的图。(无论边的数量是多少,顶点的数量都是相同的。)
Kruskal对应他,他的时间复杂度是O(eloge),与图的节点数无关,而与边数有关。因此它适用于稀疏图。(边的数量是固定的,不管顶点的数量是多少,复杂度都是一样的)
在寻找最小生成树的Prim算法中,边的权重不能为负,
typedef结构{
顶点类型adjvex
VRType低成本;
} closedge[MAX _ VERTEX _ NUM];
假设cost(u,w)表示边(U,w)上的权重,对于集合V-U中的每个顶点w,
closedge[LocateVex(G,w)]。低成本=最小{成本(U,w)|u∈U}
void MiniSpanTree_PRIM( MGraph G,VertexType u,SqList & ampMSTree)
{
// G是用数组存储表示的连通网络,由顶点U按照prim算法构造。
k = LocateVex ( G,u);
for(j = 0;j
如果(j!=k) closedge[j] = { u,G.arcs[k][j]。adj };//{adjvex,lowcost}
克洛斯奇[k]。低成本= 0;//初始状态,U={u}
for(I = 1;我
{ k =最小值(close dge);//找到T的下一个节点(图中第k个顶点)
//此时closedge[k]。低成本=
// Min{ closedge[vi]。低成本的。低成本& gt0,vi∈V-U }
printf(closedge[k]。adkvex,g . vexs[k]);//输出生成编辑器
克洛斯奇[k]。低成本= 0;//第k个顶点合并到U集中。
for(j = 0;j
if (G.arcs[k][j].adj & lt克洛斯奇[j]。lowcost) //将新顶点合并到u后重新选择最小的边。
closedge[j] = { G.vexs[k],G.arcs[k][j]。adj };
} // for
} // MiniSpanTree
拓扑排序问题
状态ToplogicalSort(代数G)
{
FindIndegree(G,in degree);//求指数中各点的渗透率[vnum];
InitStack
for(I = 0;我
if(Indegree[i]= =0)
推(S,I);
count = 0;
而(!栈顶)
{ Pop(S,I);printf(i,G.vex[i]。数据);++计数;
for(p=G..使烦恼。firstarcp;p = p-& gt;nextarc)
{ k = p-& gt;adjvex
in degree[k]-;
if( Indegree[k]= =0) push(S,k);
}//for
}//当
如果(计数
返回错误;
其他
返回OK
}
算法分析:求每个顶点的贯穿度的时间复杂度为O(e),贯穿度为零的点进入栈O(n)。在循环中,每个顶点入栈一次,出栈一次,在while***中执行1减少渗透的操作e次,所以总时间复杂度为O(n+e)。
当图中没有圈时,深度优先遍历也可以用于拓扑排序。因为图中没有圈,所以最先退出DFS函数的顶点,也就是度数为零的点,就是拓扑排序中的最后一个顶点。所以按照DFS函数的顺序记录的顶点序列就是逆拓扑有序序列。
如果你对考研有疑问,不知道考研中心的内容怎么总结,不了解考研报名的地方政策,点击最下方咨询官网,免费获取复习资料:/xl/