图算法系列-图的简单实现

最近看了很多介绍图算法的文章,发现网上可以搜到的资料比较少,所以打算在这写一个介绍图算法的系列文章,一方面是帮助自己整理,另一方面也给大家分享下这方面的知识。

1.1图的定义:
图(graph)由顶点(vertex)和边(edge)的集合组成,每一条边就是一个点对(v,w)。

图的种类:地图,电路图,调度图,事物,网络,程序结构

图的属性:有V个顶点的图最多有V*(V-1)/2条边


1.2图的ADT:


 1 struct Edge{
2 int v,w;
3 Edge(int a=-1,int b=-1):v(a),w(b){}
4 };
5
6 class Graph
7 {
8 private:
9 //
10 public:
11 Graph(int,bool);
12 ~Graph();
13 int V() const;//顶点
14 int E() const;//边
15 bool directed() const;//是否有方向
16 int insert(Edge);
17 int remove(Edge);
18 bool edge(int,int);
19 //迭代器
20 class adjIterator
21 {
22 public:
23 adjIterator(const Graph&,int);
24 int begin();
25 int next();
26 bool end();
27 };
28 };

图的构造函数将图中可能的最大顶点数作为一个参数。

1.3邻接矩阵:
邻接矩阵是一个元素为bool值的V*V矩阵,若图中存在一条连接顶点V和W的边,折矩阵adj[v][w]=1,否则为0。占用的空间为V*V,当图是稠密时,邻接矩阵是比较合适的表达方法。

//graph ADT实现(邻接矩阵)


 1 class DenseGraph
2 {
3 public:
4 DenseGraph(int v,bool digraph=false):adj(v),Vcnt(v),Ecnt(0),diGraph(digraph){
5 for(int i=0;i<v;++i)
6 adj[i].assign(v,false);//将邻接矩阵初始化为false
7 }
8 int V() const{
9 return Vcnt;
10 }
11 int E() const{
12 return Ecnt;
13 }
14 bool directed() const{
15 return diGraph;
16 }
17 void insert(Edge e)
18 {
19 int v=e.v,w=e.w;
20 if(adj[v][w]==false) Ecnt++;
21 adj[v][w]=true;
22 if(!digraph) adj[w][v]=true;
23 }
24 void remove(Edge e)
25 {
26
27 int v=e.v,w=e.w;
28 if(adj[v][w]==true) Ecnt--;
29 adj[v][w]=false;
30 if(!digraph) adj[w][v]=false;
31 }
32 bool edge(int v,int w) const
33 {
34 return adj[v][w];
35 }
36 class adjIterator;
37 friend class adjIterator;
38
39 private:
40 int Vcnt;//顶点数
41 int Ecnt;//边数
42 bool diGraph;//是否有向图
43 vector< vector<bool> > adj;//邻接矩阵
44 };
45
46 //邻接矩阵的迭代器,返回顶点vertex的下一个相邻的顶点
47 class DenseGraph::adjIterator
48 {
49 public:
50 adjIterator(const DenseGraph &g,int vertex):
51 G(g),v(vertex),i(-1){}
52 int begin()
53 {
54 i=-1;
55 return next();
56 }
57 int next()
58 {
59 for(i++;i<G.V();++i)
60 if(G.adj[v][i]==true) return i;
61 return -1;
62 }
63 bool end()
64 {
65 return i>=G.V();
66 }
67 private:
68 const DenseGraph &G;
69 int i,v;
70 };

1.4邻接表的表示
对于非稠密的图,使用邻接矩阵有点浪费存储空间,可以使用邻接表,我们维护一个链表向量,给定一个顶点时,可以立即访问其链表,占用的空间为O(V+E)。

//程序:Graph的邻接表实现


 1 class SparseGraph()
2 {
3 public:
4 SparseGraph(int v,bool digraph=false):
5 Vcnt(v),Ecnt(0),diGraph(digraph){
6 adj.assign(v,0);
7 }
8 SparseGraph(const SparseGraph& G);
9 ~SparseGraph();
10 int V() const{
11 return Vcnt;
12 }
13 int E() const{
14 return Ecnt;
15 }
16 bool directed(){
17 return digraph;
18 }
19 //往图中插入一条边
20 void insert(Edge e){
21 int v=e.v,w=e.w;
22 adj[v]=new node(w,adj[v]);
23 if(!diGraph) adj[w]=new node(v,adj[w]);
24 Ecnt++;
25 }
26 void remove(Edge e);
27 bool Edge(int v,int w) const;
28 class adjIterator;//邻接表的迭代器
29 friend class adjIterator;//把迭代器类设置为友元类
30 private:
31 int Vcnt,Ecnt;
32 bool diGraph;
33 struct node
34 {
35 int v;
36 node* next;
37 node(int x,node* t):v(x),next(t){}
38 };
39 typedef node* link;
40 vector<link> adj;//邻接表
41 };
42
43 //邻接表迭代器的实现
44 class SparseGraph::adjIterator
45 {
46 public:
47 adjIterator(const SparseGraph& g,int v):
48 G(g),v(v),curLink(0){}
49 adjIterator(const adjIterator&);
50 ~adjIterator();
51 int begin(){
52 curLink=adj[v];
53 return curLink?curLink-v:-1;
54
55 }
56 int next(){
57 if(curLink) curLink=curLink->next;
58 return curLink?curLink->v:-1;
59 }
60 bool end(){
61 return curLink==0;
62 }
63 private:
64 const SparseGraph& G;
65 int v;//顶点编号
66 link curLink;
67 };

1.5简单路径搜索
给定两个顶点,图中是否存在一条连接起来的简单路径呢?
比如给定顶点V和W,对于和V相邻的每个顶点t,是否存在从t到W的简单路径,然后递归调用,我们使用一个顶点访问向量visited来对顶点标记防止重复访问。

//程序:简单路径搜索


 1 template <class Graph> class SearchPath
2 {
3
4 public:
5 SearchPath(const Graph& g,int v,int w):
6 G(g),visited(g.V(),false){
7 found=search(v,w);
8 }
9 bool existed() const
10 {
11 return found;
12 }
13 private:
14 const Graph& G;
15 vector<bool> visited;
16 bool found;
17 bool search(int v,int w)
18 {
19 if(v=w) return true;
20 visited[v]=true;

21 typename Graph::adjIterator ite(G,v);
22 for(int t=ite.begin();!ite.end();t=ite.next())
23 if(!visited[t])
24 if(search(t,w)) return true;
25 return false;
26 }
27 };

参考资料:维基百科http://en.wikipedia.org/wiki/Category:Graph_algorithms

      Graph
algorithm:http://www-users.cs.umn.edu/~karypis/parbook/Lectures/AG/chap10_slides.pdf

      

时间: 2024-11-08 19:48:50

图算法系列-图的简单实现的相关文章

图算法系列-深度优先搜索与广度优先搜索

2.深度优先搜索 为了访问一个顶点,我们将它标记为已经访问过,然后递归的访问所有与子邻接的并且尚未标记的顶点,这就是深度优先搜索(DFS),DFS常用于解决路径问题. 比如下面的连通图,我们从顶点0开始对图进行探索 下面这个图显示了DFS处理时的递归调用树. DFS可以解决的问题:1)环检测:一个图中有环吗?该图是森林吗?2)简单路径:给定两个顶点,是否存在一条连接他们的路径3)简单连通性:无论何时使用DFS,都可以在线性时间内确定一个图是否连通4)顶点搜索:在给定顶点所在的同一个连通分量中有多

【iOS开发系列】用简单工厂模式理解OC反射机制

// 在iOS开发中,简单工厂模式使用得并不多.但是.我认为这是OC反射机制很好的一个例子, // 所以本文将以计算器为例,讲解简单工厂模式和OC的反射机制. // [简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类( // 这些产品类继承自一个父类或接口)的实例.该模式中包含的角色及其职责:工厂角色.抽 // 象产品角色.具体产品角色] // --百度百科 简单工厂模式 // 上面这句话可能不怎么好理解,我在网上找到了一个例子,可能例子本身不能完全解释这个 // 设

Cisco 3640系列IPsec VPN简单配置

Cisco 3640系列IPsec VPN简单配置 实验拓扑图: 实验步骤: 第一步:配置路由 第二步:匹配的数据包流量(ACL,注意两端要对称) 第三步:IKE的第一阶段(称为 ISAKMP SA) 第四步:IKE的第二阶段(称为IPSEC SA) 第五步:MAP(第二.三.四步的结合) 第六步:应用配置到外网口 模拟Internet步骤如下: 1.路由配置 R1: Router>en Router#conft Enterconfiguration commands, one per line

posix 线程(一):线程模型、pthread 系列函数 和 简单多线程服务器端程序

posix 线程(一):线程模型.pthread 系列函数 和 简单多线程服务器端程序 一.线程有3种模型,分别是N:1用户线程模型,1:1核心线程模型和N:M混合线程模型,posix thread属于1:1模型. (一).N:1用户线程模型 “线程实现”建立在“进程控制”机制之上,由用户空间的程序库来管理.OS内核完全不知道线程信息.这些线程称为用户空间线程.这些线程都工作在“进 程竞争范围”(process contention scope):各个线程在同一进程竞争“被调度的CPU时间”(但

设计模式系列一(简单工厂VS工厂方法)

1.简单工厂简介 诞生背景:在我们平常编程当中,经常会使用new进行实例化一个对象,此时该类完全依赖于该对象,专业术语来说就是耦合度高.当需求发生变化时我们不得不去修改此类的源码,造成整个系统难以维护!然而面向对象(oo)一个很重要的原则(封装变化)就可以解决这样一个问题:那既然要封装变化,自然要找到改变的代码,然后把改变的代码用类进行封装.这样一个思路就引出了简单工厂模式. 定义:简单工厂模式又叫静态工厂模式,顾名思义,它是用来实例化对象类的静态类. 2.简单工厂实例 /// <summary

Net系列框架-Dapper+简单三层架构

Net系列框架-Dapper+简单三层架构 工作将近6年多了,工作中也陆陆续续学习和搭建了不少的框架,后续将按由浅入深的方式,整理出一些框架源码,所有框架源码本人都亲自调试通过,如果有问题,欢迎联系我,供大家一起互相学习和探讨,如果你有什么好的意见或建议,欢迎下面留言. 本套框架主要采用asp.net mvc+dapper+简单三层搭建的后端框架 主要技术点: 1.asp.net webapi 基于net技术的webapi 2.dapper 轻量高性能orm框架 3.三层架构,简单实用的DAL,

SSE图像算法优化系列十:简单的一个肤色检测算法的SSE优化。

在很多场合需要高效率的肤色检测代码,本人常用的一个C++版本的代码如下所示: void IM_GetRoughSkinRegion(unsigned char *Src, unsigned char *Skin, int Width, int Height, int Stride) { for (int Y = 0; Y < Height; Y++) { unsigned char *LinePS = Src + Y * Stride; // 源图的第Y行像素的首地址 unsigned char

8天掌握EF的Code First开发系列之2 简单的CRUD操作

本文出自8天掌握EF的Code First开发系列,经过自己的实践整理出来. 本篇目录 创建控制台项目 根据.Net中的类来创建数据库 简单的CRUD操作 数据库模式更改介绍 本章小结 本人的实验环境是VS 2012,windows 7,MSSQL Server 2008 R2. 创建控制台项目 1. 新建控制台应用项目 2. 通过NuGet安装Entity Framework 6 根据.Net中的类来创建数据库 上面的步骤之后,我们就可以开始写代码了.在写代码之前,你要始终记得,每个类就是相应

PS扣图的简单整理

一.魔术棒法——最直观的方法 适用范围:图像和背景色色差明显,背景色单一,图像边界清晰. 方法意图:通过删除背景色来获取图像. 方法缺陷:对散乱的毛发没有用. 使用方法:1.点击“魔术棒”工具: 2.在“魔术棒”工具条中,在“连续”项前打勾: 3.“容差”值填入“20”.(值可以看之后的效果好坏进行调节): 4.用魔术棒点背景色,会出现虚框围住背景色: 5.如果对虚框的范围不满意,可以先按CTRL+D取消虚框,再对上一步的“容差”值进行调节: 6.如果对虚框范围满意,按键盘上的DELE键,删除背