图的算法框架

-------------------siwuxie095

图的算法框架

图的算法可以写在函数中,也可以封装在类中,为了严谨起见

和后续复用,这里统一将图的算法都封装在类中

其实对于图的算法而言,通常会比较复杂,需要很多辅助数据

结构,而且这些数据结构可能会成为类中的成员变量,这也是

要将图的算法封装在类中的原因之一

同时,这里也会将封装成的类都设置为类模板,这样,不管是

稀疏图,还是稠密图,都可以传入算法中,从而形成模板类

如:从文件中读取图的测试用例的算法

文件
testG1.txt 的内容,如下:

该文件可以分成两部分:

1)第一行:(13,13),表示图中有 13 个顶点,13 条边

2)其它行:每一行有两个数字,表示一条边。共 13 行,即有 13 条边

程序:

SparseGraph.h:


#ifndef SPARSEGRAPH_H

#define SPARSEGRAPH_H

#include <iostream>

#include <vector>

#include <cassert>

using namespace std;

// 稀疏图 - 邻接表

class SparseGraph

{

private:

int n, m; //n 和 m 分别表示顶点数和边数

bool directed; //directed表示是有向图还是无向图

vector<vector<int>> g; //g[i]里存储的就是和顶点i相邻的所有顶点

public:

SparseGraph(int n, bool directed)

{

//初始化时,有n个顶点,0条边

this->n = n;

this->m = 0;

this->directed = directed;

//g[i]初始化为空的vector

for (int i = 0; i < n; i++)

{

g.push_back(vector<int>());

}

}

~SparseGraph()

{

}

int V(){ return n; }

int E(){ return m; }

//在顶点v和顶点w之间建立一条边

void addEdge(int v, int w)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

g[v].push_back(w);

//(1)顶点v不等于顶点w,即
不是自环边

//(2)且不是有向图,即
是无向图

if (v != w && !directed)

{

g[w].push_back(v);

}

m++;

}

//hasEdge()判断顶点v和顶点w之间是否有边

//hasEdge()的时间复杂度:O(n)

bool hasEdge(int v, int w)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

for (int i = 0; i < g[v].size(); i++)

{

if (g[v][i] == w)

{

return true;

}

}

return false;

}

void show()

{

for (int i = 0; i < n; i++)

{

cout << "vertex " << i << ":\t";

for (int j = 0; j < g[i].size(); j++)

{

cout << g[i][j] << "\t";

}

cout << endl;

}

}

//相邻点迭代器(相邻,即 adjacent)

//

//使用迭代器可以隐藏迭代的过程,按照一定的

//顺序访问一个容器中的所有元素

class adjIterator

{

private:

SparseGraph &G; //图的引用,即
要迭代的图

int v; //顶点v

int index; //相邻顶点的索引

public:

adjIterator(SparseGraph &graph, int v) : G(graph)

{

this->v = v;

this->index = 0;

}

//要迭代的第一个元素

int begin()

{

//因为有可能多次调用begin(),

//所以显式的将index设置为0

index = 0;

//如果g[v]的size()不为0

if (G.g[v].size())

{

return G.g[v][index];

}

return -1;

}

//要迭代的下一个元素

int next()

{

index++;

if (index < G.g[v].size())

{

return G.g[v][index];

}

return -1;

}

//判断迭代是否终止

bool end()

{

return index >= G.g[v].size();

}

};

};

//事实上,平行边的问题,就是邻接表的一个缺点

//

//如果要在addEdge()中判断hasEdge(),因为hasEdge()是O(n)的复

//杂度,那么addEdge()也就变成O(n)的复杂度了

//

//由于在使用邻接表表示稀疏图时,取消平行边(即
在addEdge()

//中加上hasEdge()),相应的成本比较高

//

//所以,通常情况下,在addEdge()函数中就先不管平行边的问题,

//也就是允许有平行边。如果真的要让图中没有平行边,就在所有

//边都添加进来之后,再进行一次综合的处理,将平行边删除掉

#endif

DenseGraph.h:


#ifndef DENSEGRAPH_H

#define DENSEGRAPH_H

#include <iostream>

#include <vector>

#include <cassert>

using namespace std;

// 稠密图 - 邻接矩阵

class DenseGraph

{

private:

int n, m; //n 和 m 分别表示顶点数和边数

bool directed; //directed表示是有向图还是无向图

vector<vector<bool>> g; //二维矩阵,存放布尔值,表示是否有边

public:

DenseGraph(int n, bool directed)

{

//初始化时,有n个顶点,0条边

this->n = n;

this->m = 0;

this->directed = directed;

//二维矩阵:n行n列,全部初始化为false

for (int i = 0; i < n; i++)

{

g.push_back(vector<bool>(n, false));

}

}

~DenseGraph()

{

}

int V(){ return n; }

int E(){ return m; }

//在顶点v和顶点w之间建立一条边

void addEdge(int v, int w)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

//如果顶点v和顶点w之间已经存在一条边,

//则直接返回,即排除了平行边

if (hasEdge(v, w))

{

return;

}

g[v][w] = true;

//如果是无向图,则g[w][v]处也设为true(无向图沿主对角线对称)

if (!directed)

{

g[w][v] = true;

}

m++;

}

//hasEdge()判断顶点v和顶点w之间是否有边

//hasEdge()的时间复杂度:O(1)

bool hasEdge(int v, int w)

{

assert(v >= 0 && v < n);

assert(w >= 0 && w < n);

return g[v][w];

}

void show()

{

for (int i = 0; i < n; i++)

{

for (int j = 0; j < n; j++)

{

cout << g[i][j] << "\t";

}

cout << endl;

}

}

//相邻点迭代器(相邻,即 adjacent)

class adjIterator

{

private:

DenseGraph &G; //图的引用,即
要迭代的图

int v; //顶点v

int index; //相邻顶点的索引

public:

adjIterator(DenseGraph &graph, int v) : G(graph)

{

this->v = v;

this->index = -1;

}

//要迭代的第一个元素

int begin()

{

//找第一个为true的元素,即为要迭代的第一个元素

index = -1;

return next();

}

//要迭代的下一个元素

int next()

{

for (index += 1; index < G.V(); index++)

{

if (G.g[v][index])

{

return index;

}

}

return -1;

}

//判断迭代是否终止

bool end()

{

return index >= G.V();

}

};

};

//addEdge()函数隐含着:当使用邻接矩阵表示稠密图时,已经

//不自觉的将平行边给去掉了,即
在添加边时,如果发现已经

//存在该边,就不做任何操作,直接返回即可

//

//事实上,这也是使用邻接矩阵的一个优势可以非常方便的处理

//平行边的问题

//

//另外,由于使用的是邻接矩阵,可以非常快速的用O(1)的方式,

//来判断顶点v和顶点w之间是否有边

#endif

ReadGraph.h:


#ifndef READGRAPH_H

#define READGRAPH_H

#include <iostream>

#include <string>

#include <fstream>

#include <sstream>

#include <cassert>

using namespace std;

//从文件中读取图的测试用例

template <typename Graph>

class ReadGraph

{

public:

ReadGraph(Graph &graph, const string &filename)

{

ifstream file(filename);

string line; //一行一行的读取

int V, E;

assert(file.is_open());

//读取file中的第一行到line中

assert(getline(file, line));

//将字符串line放在stringstream中

stringstream ss(line);

//通过stringstream解析出整型变量:顶点数和边数

ss >> V >> E;

//确保文件里的顶点数和图的构造函数中传入的顶点数一致

assert(V == graph.V());

//读取file中的其它行

for (int i = 0; i < E; i++)

{

assert(getline(file, line));

stringstream ss(line);

int a, b;

ss >> a >> b;

assert(a >= 0 && a < V);

assert(b >= 0 && b < V);

graph.addEdge(a, b);

}

}

};

#endif

main.cpp:


#include
"SparseGraph.h"

#include
"DenseGraph.h"

#include
"ReadGraph.h"

#include <iostream>

using namespace std;

int main()

{

string filename = "testG1.txt";

SparseGraph g1(13, false);

ReadGraph<SparseGraph> readGraph1(g1, filename);

g1.show();

cout << endl;

DenseGraph g2(13, false);

ReadGraph<DenseGraph> readGraph2(g2, filename);

g2.show();

system("pause");

return
0;

}

【made by siwuxie095】

时间: 2024-10-29 07:03:40

图的算法框架的相关文章

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

回溯0--递归回溯算法框架

递归回溯算法框架 一.心得 3 都是在for下的if下的 4 保存结果,找下一步,回溯,这三个是一起的 5 还有一个到达目的地输出解放在外面就好 search后面的k是轮数  三个数组:原数据数组标记数组储存结果数组 框架二 到目的地的情况要多加1,因为满足了的下一轮就好判断 二.代码 1 /* 2 递归回溯算法框架: 3 都是在for下的if下的 4 保存结果,找下一步,回溯,这三个是一起的 5 还有一个到达目的地输出解放在外面就好 6 */ 7 /* 8 框架一 9 */ 10 int se

图的算法

因为图算法的各种英文名傻傻分不清楚,所以总结一下. 求最小生成树: Kruskal 假设给定一个加权连通图G,G的边集合为E,顶点个数为n,要求其一棵最小生成树T. 假设T中的边和顶点均涂成红色,其余边为白色.开始时G中的边均为白色. 1)将所有顶点涂成红色: 2)在白色边中,挑选一条权最小的边,使其与红色边不形成圈,将该白色边涂红: 3)重复2)直到有n-1条红色边,这n-1条红色边便构成最小生成树T的边集合. prime算法 1.清空生成树,任取一个顶点加入生成树 2.在那些一个端点在生成树

Geeks Union-Find Algorithm Union By Rank and Path Compression 图环算法

同样是查找一个图是否有环的算法,但是这个算法很牛逼,构造树的时候可以达到O(lgn)时间效率.n代表顶点数 原因是根据需要缩减了树的高度,也叫压缩路径(Path compression),名字很高深,不过其实不难理解,简单来说就是每次查找一个节点的时候,都把这一路径中的所有节点都赋予根节点作为路径. 原文没指出的地方: 也因为需要压缩,所以初始化的时候注意,不能如前面简单实用Union Find的算法那样初始化所有顶点的父母节点为-1,应该是初始化所有节点的父母节点为本身(自己繁殖自己?),然后

图基本算法 图的表示方法 邻接矩阵 邻接表

要表示一个图G=(V,E),有两种标准的表示方法,即邻接表和邻接矩阵.这两种表示法既可用于有向图,也可用于无向图.通常采用邻接表表示法,因为用这种方法表示稀疏图(图中边数远小于点个数)比较紧凑.但当遇到稠密图(|E|接近于|V|^2)或必须很快判别两个给定顶点手否存在连接边时,通常采用邻接矩阵表示法,例如求最短路径算法中,就采用邻接矩阵表示. 图G=<V,E>的邻接表表示是由一个包含|V|个列表的数组Adj所组成,其中每个列表对应于V中的一个顶点.对于每一个u∈V,邻接表Adj[u]包含所有满

基于GraphCuts图割算法的图像分割----OpenCV代码与实现

部分代码与文档是早些时候收集的,出处找不到了,还请原作者看到后联系注明. 图切算法是组合图论的经典算法之一.近年来,许多学者将其应用到图像和视频分割中,取得了很好的效果.本文简单介绍了图切算法和交互式图像分割技术,以及图切算法在交互式图像分割中的应用. 图像分割指图像分成各具特性的区域并提取出感兴趣目标的技术和过程,它是由图像处理到图像分析的关键步骤,是一种基本的计算机视觉技术.只有在图像分割的基础上才能对目标进行特征提取和参数测量,使得更高层的图像分析和理解成为可能.因此对图像分割方法的研究具

图基本算法 图搜索(广度优先、深度优先)

(边自学边写,还真有点累啊,) 注:以下代码均为部分,关于图的表示方法参看我的博客: http://www.cnblogs.com/dzkang2011/p/graph_1.html 一.广度优先搜索 广度优先搜索(BFS)是最简单的图搜索算法之一,也是很多重要的图算法的原型.在Prim最小生成树算法和Dijkstra单源最短路径算法中,都采用了与广度优先搜索类似的思想. 在给定图G=(V,E)和一个特定的源顶点s的情况下,广度优先搜索系统地探索G中的边,以期发现可以从s到达的所有顶点,并计算s

【E2LSH源码分析】LSH算法框架分析

位置敏感哈希(Locality Sensitive Hashing,LSH)是近似最近邻搜索算法中最流行的一种,它有坚实的理论依据并且在高维数据空间中表现优异.由于网络上相关知识的介绍比较单一,现就LSH的相关算法和技术做一介绍总结,希望能给感兴趣的朋友提供便利,也希望有兴趣的同道中人多交流.多指正. 1.LSH原理 最近邻问题(nearest neighbor problem)可以定义如下:给定n个对象的集合并建立一个数据结构,当给定任意的要查询对象时,该数据结构返回针对查询对象的最相似的数据

JAVA 图作业算法实现、代写Graphs 数据结构作业

JAVA 图作业算法实现.代写Graphs 数据结构作业Lab Case – Algorithms and Data Structure, 2017-2018Phase 3. GraphsCurrently, SharingCar only provides service in ten cities (Madrid, Barcelona, Valencia, Sevilla, Bilbao, Granada, Toledo, Salamanca, Alicante, Cáceres). Not