ImageVerifierCode 换一换
格式:DOCX , 页数:27 ,大小:30.46KB ,
资源ID:4607154      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/4607154.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(数据结构中图的应用.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

数据结构中图的应用.docx

1、数据结构中图的应用 图在数据结构中应用十分广泛,对于图来说最重要的当然是算法,而且相当的一部分都是很专业的,一般的人几乎不会接触到;相对而言,结构就显得分量很轻。你可以看到关于图中元素的操作很少,远没有单链表那里列出的一大堆“接口”。一个结构如果复杂,那么能确切定义的操作就很有限。基本储存方法不管怎么说,还是先得把图存起来。不要看书上列出了好多方法,根本只有一个邻接矩阵。如果矩阵是稀疏的,那就可以用十字链表来储存矩阵(见前面的稀疏矩阵(十字链表)。如果我们只关系行的关系,那么就是邻接表(出边表);反之,只关心列的关系,就是逆邻接表(入边表)。下面给出两种储存方法的实现。#ifndef Grap

2、hmem_H#define Graphmem_H#include #include using namespace std;template class Network;const int maxV = 20;/最大节点数template class AdjMatrixfriend class Networkname, dist, AdjMatrix ;public:AdjMatrix() : vNum(0), eNum(0)vertex = new namemaxV; edge = new dist*maxV;for (int i = 0; i maxV; i+) edgei = new d

3、istmaxV;AdjMatrix()for (int i = 0; i maxV; i+) delete edgei;delete edge; delete vertex;bool insertV(name v)if (find(v) return false;vertexvNum = v;for (int i = 0; i maxV; i+) edgevNumi = NoEdge;vNum+; return true;bool insertE(name v1, name v2, dist cost)int i, j;if (v1 = v2 | !find(v1, i) | !find(v2

4、, j) return false;if (edgeij != NoEdge) return false;edgeij = cost; eNum+; return true;name& getV(int n) return vertexn; /没有越界检查int nextV(int m, int n)/返回m号顶点的第n号顶点后第一个邻接顶点号,无返回-1for (int i = n + 1; i vNum; i+) if (edgemi != NoEdge) return i;return -1;private:int vNum, eNum;dist NoEdge, *edge; name

5、*vertex;bool find(const name& v)for (int i = 0; i vNum; i+) if (v = vertexi) return true;return false;bool find(const name& v, int& i)for (i = 0; i vNum; i+) if (v = vertexi) return true;return false;template class LinkedListfriend class Networkname, dist, LinkedList ;public:LinkedList() : vNum(0),

6、eNum(0) LinkedList()for (int i = 0; i vNum; i+) delete verticesi.e;bool insertV(name v)if (find(v) return false;vertices.push_back(vertex(v, new list);vNum+; return true;bool insertE(const name& v1, const name& v2, const dist& cost)int i, j;if (v1 = v2 | !find(v1, i) | !find(v2, j) return false;for

7、(list:iterator iter = verticesi.e-begin();iter != verticesi.e-end() & iter-vID end()verticesi.e-push_back(edge(j, cost); eNum+; return true;if (iter-vID = j) return false;verticesi.e-insert(iter, edge(j, cost); eNum+; return true;name& getV(int n) return verticesn.v; /没有越界检查int nextV(int m, int n)/返

8、回m号顶点的第n号顶点后第一个邻接顶点号,无返回-1for (list:iterator iter = verticesm.e-begin();iter != verticesm.e-end(); iter+) if (iter-vID n) return iter-vID;return -1;private:bool find(const name& v)for (int i = 0; i vNum; i+) if (v = verticesi.v) return true;return false;bool find(const name& v, int& i)for (i = 0; i

9、vNum; i+) if (v = verticesi.v) return true;return false;struct edgeedge() edge(int vID, dist cost) : vID(vID), cost(cost) int vID;dist cost;struct vertexvertex() vertex(name v, list* e) : v(v), e(e) name v;list* e;int vNum, eNum;vector vertices;#endif这个实现是很简陋的,但应该能满足后面的讲解了。现在这个还什么都不能做,不要急,在下篇将讲述图的DF

10、S和BFSDFS和BFS对于非线性的结构,遍历都会首先成为一个问题。和二叉树的遍历一样,图也有深度优先搜索(DFS)和广度优先搜索(BFS)两种。不同的是,图中每个顶点没有了祖先和子孙的关系,因此,前序、中序、后序不再有意义了。仿照二叉树的遍历,很容易就能完成DFS和BFS,只是要注意图中可能有回路,因此,必须对访问过的顶点做标记。 最基本的有向带权网#ifndef Graph_H #define Graph_H #include #include using namespace std; #include Graphmem.h template class Network public: N

11、etwork() Network(dist maxdist) data.NoEdge = maxdist; Network() bool insertV(name v) return data.insertV(v); bool insertE(name v1, name v2, dist cost) return data.insertE(v1, v2, cost); name& getV(int n) return data.getV(n); int nextV(int m, int n = -1) return data.nextV(m, n); int vNum() return dat

12、a.vNum; int eNum() return data.eNum; protected: bool* visited; static void print(name v) cout v; private: mem data; ; #endif 你可以看到,这是在以mem方式储存的data上面加了一层外壳。在图这里,逻辑上分有向、无向,带权、不带权;储存结构上有邻接矩阵和邻接表。也就是说分开来有8个类。为了最大限度的复用代码,继承关系就非常复杂了。但是,多重继承是件很讨厌的事,什么覆盖啊,还有什么虚拟继承,我可不想花大量篇幅讲语言特性。于是,我将储存方式作为第三个模板参数,这样一来就省得涉

13、及虚拟继承了,只是这样一来这个Network的实例化就很麻烦了,不过这可以通过typedef或者外壳类来解决,我就不写了。反正只是为了让大家明白,真正要用的时候,最好是写专门的类,比如无向无权邻接矩阵图,不要搞的继承关系乱七八糟。 DFS和BFS的实现public: void DFS(void(*visit)(name v) = print) visited = new boolvNum(); for (int i = 0; i vNum(); i+) visitedi = false; DFS(0, visit); delete visited; protected: void DFS(in

14、t i, void(*visit)(name v) = print) visit(getV(i); visitedi = true; for (int n = nextV(i); n != -1; n = nextV(i, n) if (!visitedn) DFS(n, visit); public: void BFS(int i = 0, void(*visit)(name v) = print)/n没有越界检查 visited = new boolvNum(); queue a; int n; for (n = 0; n vNum(); n+) visitedn = false; vis

15、itedi = true; while (i != -1)/这个判断可能是无用的 visit(getV(i); for (n = nextV(i); n != -1; n = nextV(i, n) if (!visitedn) a.push(n); visitedn = true; if (a.empty() break; i = a.front(); a.pop(); delete visited; DFS和BFS函数很难写得像树的遍历方法那么通用,这在后面就会看到,虽然我们使用了DFS和BFS的思想,但是上面的函数却不能直接使用。因为树的信息主要在节点上,而图的边上还有信息。 测试程序#

16、include using namespace std; #include Graph.h int main() Networkchar, int, LinkedList a; a.insertV(A); a.insertV(B); a.insertV(C); a.insertV(D); a.insertE(A, B, 1); a.insertE(A, C, 2); a.insertE(B, D, 3); cout DFS: ; a.DFS(); cout endl; cout BFS: ; a.BFS(); cout endl; return 0; 老实说,这个类用起来真的不是很方便。不过能

17、说明问题就好。 无向图要是在纸上随便画画,或者只是对图做点示范性的说明,大多数人都会选择无向图。然而在计算机中,无向图却是按照有向图的方法来储存的存两条有向边。实际上,当我们说到无向的时候,只是忽略方向在纸上画一条线,难不成那线“嗖”的就出现了,不是从一头到另一头画出来的? 无向图有几个特有的概念,连通分量、关节点、最小生成树。下面将分别介绍,在此之前,先完成无向图类的基本操作。无向图类template class Graph : public Networkpublic:Graph() Graph(dist maxdist) : Network (maxdist) bool insertE(

18、name v1, name v2, dist cost)if (Network:insertE(v1, v2, cost)return Network:insertE(v2, v1, cost);return false;仅仅是添加边的时候,再添加一条反向边,很简单。连通分量这是无向图特有的,有向图可要复杂多了(强、单、弱连通),原因就是无向图的边怎么走都行,有向图的边好像掉下无底深渊就再也爬不上来了。有了DFS,求连通分量的算法就变得非常简单了对每个没有访问的顶点调用DFS就可以了。void components()visited = new boolvNum(); int i, j = 0

19、;for (i = 0; i vNum(); i+) visitedi = false;cout Components: endl;for (i = 0; i vNum(); i+)if (!visitedi) cout ( +j ); DFS(i); cout = dfni) cout =就显得很尴尬了,因为只能等于不可能大于。还要注意的是,生成树的根(DFS的起始点)是单独判断的。void articul()dfn = new intvNum(); low = new intvNum(); int i, j = 0, n;for(i = 0; i vNum(); i+) dfni = lo

20、wi = 0;/初始化for (i = 0; i vNum(); i+)if (!dfni)cout ( +j ); dfni = lowi = count = 1;if (n = nextV(i) != -1) articul(n); bool out = false;/访问树根while (n = nextV(i, n) != -1)if (dfnn) continue;if (!out) cout getV(i); out = true; /树根有不只一个子女articul(n);/访问其他子女cout endl;delete dfn; delete low;private:void a

21、rticul(int i)dfni = lowi = +count;for (int n = nextV(i); n != -1; n = nextV(i, n)if (!dfnn)articul(n);if (lown = dfni) cout getV(i);/这里只可能else if (dfnn lowi) lowi = dfnn;/回边判断int *dfn, *low, count; 最小生成树说人是最难伺候的,真是一点不假。上面刚刚为了“提高可靠性”添加了几条多余的边,这会儿又来想办法怎么能以最小的代价把所有的顶点都连起来。可能正是人的这种精神才使得人类能够进步吧看着现在3GHz的C

22、PU真是眼红啊,我还在受500MHz的煎熬,然后再想想8086 正如图的基本元素是顶点和边,从这两个方向出发,就能得到两个算法Kruskal算法(从边出发)、Prim算法(从顶点出发)。据说还有别的方法,恕我参考资料有限,不能详查。 最小生成树的储存显然用常用的树的储存方法来储存没有必要,虽然名曰“树”,实际上,这里谁是谁的“祖先”、“子孙”并不重要。因此,用如下的MSTedge结构数组来储存就可以了。 template class MSTedge public: MSTedge() MSTedge(int v1, int v2, dist cost) : v1(v1), v2(v2), co

23、st(cost) int v1, v2; dist cost; bool operator (const MSTedge& v2) return (cost v2.cost); bool operator (const MSTedge& v2) return (cost v2.cost); bool operator = (const MSTedge& v2) return (cost = v2.cost); ; Kruskal算法最小生成树直白的讲就是,挑选N1条不产生回路最短的边。Kruskal算法算是最直接的表达了这个思想在剩余边中挑选一条最短的边,看是否产生回路,是放弃,不是选定然后重

24、复这个步骤。说起来倒是很简单,做起来就不那么容易了判断是否产生回路需要并查集,在剩余边中找一条最短的边需要最小堆(并不需要对所有边排序,所以堆是最佳选择)。 Kruskal算法的复杂度是O(eloge),当e接近N2时,可以看到这个算法不如O(N2)的Prim算法,因此,他适合于稀疏图。而作为稀疏图,通常用邻接表来储存比较好。另外,对于邻接矩阵储存的图,Kruskal算法比Prim算法占不到什么便宜(初始还要扫描N2条“边”)。因此,最好把Kruskal算法放在Link类里面。 template int Link:MinSpanTree(MSTedge* a) MinHeapMSTedge E; int i, j, k, l = 0; MFSets V(vNum); list:iterator iter; for (i = 0; i begin(); iter != verticesi.e-end(); iter+) E.insert(MSTedge(i, iter-vID, iter-cost);/建立边的堆 for (i = 0; i eNum & l vNum; i+)/Kruskal Start j = V.find(E.top()

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1