本章由浅入深地学习了图。
图(GRAPH)的定义:是一种非线性数据结构,由有穷、非空的点集V(G)和边集E(G)组成。当G中的每条边有方向时,称G为有向图,有向边(用一对尖括号<a,b>)又称为弧,起始顶点被称为弧尾,终止顶点被称为弧头,每条边无方向时(用一对括号表示(a,b)和(b,a)一样),被称为无向图。
图中顶点和边的关系:
有向图:顶点n和边数e,满足0<=e<=n(n-1),若e = n(n-1),称为完全有向图:每个顶点之间都有两条互为相反的边的无向图;
无向图:顶点n和边数e,满足0<=e<=n(n-1)/2,若e = n(n-1)/2,称为完全无向图:每个顶点之间都有一条边的无向图;
若图中e < n lbn 称该图为稀疏图,否则为稠密图;
由于图的结构比较复杂,所以一般采用二维数组(即邻接矩阵),或者是链表的存储形式。一般当图为稠密图时多采用链表的存储方式,避免浪费空间。
本章的重点之一是BFS算法和DFS算法。其中DFS算法是:从图中某一顶点出发,访问后标记visit[i]为1,然后依次搜索第i个结点的领接点j,再依次搜索j结点的每个领接点,直到所有结点都被遍历。
BFS算法是:先被访问的结点,其领接点也先被访问,具有先进先出的特性,我们使用队列来保存已经访问过的结点,以确定被访问过结点的顶点领接点访问次序。
另外老师在课上讲了一些关于普里姆算法的内容,课后我将其补齐了。普里姆算法是用于求最小生成树的一种算法。
简单来说普里姆算法分为以下五步:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
图源自wiki。
如以上三个图。我们选取D为起始点,那么根据普里姆算法,集合E中的最小边是AD(权值为5)顶点为A并且A不在集合V中,所以将AD相连并且将A加入到V集合中,如此反复就可以得到最小生成树。
在分享下wiki上的普里姆算法C语言的主体部分
//来源:严蔚敏 吴伟民《数据结构(C语言版)》 void MiniSpanTree_PRIM (MGraph G, VertexType u) { /* 用普利姆算法從第u個頂點出發構造網G 的最小生成樹T,輸出T的各條邊。 記錄從頂點集U到V-U的代價最小的邊的輔助數組定義: struct { VertexType adjvex; VRtype lowcost; }closedge[MAX_VERTEX_NUM]; */ k = LocateVex(G, u); for (j = 0 ; j < G.vexnum; j++) { //輔助數組初始化 if (j != k) closedge[j] = {u, G.arcs[k][j].adj}; //{adjvex, lowcost} } closedge[k].lowcost = 0; //初始,U={u} for (i = 1; i < G.vexnum ; i++) { //選擇其餘G.vexnum -1 個頂點 k = minimum(closedge); //求出T的下個結點:第k結點 // 此时 closedge[k].lowcost = MIN{ closedge[Vi].lowcost|closedge[Vi].lowcost>0,Vi∈V-U} printf(closedge[k].adjvex, G.vexs[k]); //輸出生成樹的邊 closedge[k].lowcost = 0; //第k條邊併入U集 for (j = 0; j < G.vexnum; j++) { if (G.arcs[k][j].adj < closedge[j].lowcost && closedge[j].lowcost!=0) //新頂點併入U後重新選擇最小邊 closedge[j] = {G.vex[k], G.arcs[k][j].adj}; } } }View Code