阅读 46

离散数学简单图的判断,什么叫简单无向图

根据性质,图可以分为有向图和有向图。 本文首先介绍有向图,然后介绍有向图。 研究图是因为图在生活中被广泛使用。

无向图

图由几个顶点(Vertices )和边(edge )相互连接而成。 边只由两个顶点连接而没有方向的图称为无向图。 在讨论图之前,有几个定义需要澄清。 下图显示了图中基本属性的含义,这里不多赘述。

图的API显示

在研究图之前,我们需要选择合适的数据结构来表示图。 有时,它经常被我们的直觉所欺骗。 如下图所示,这两个其实是一样的。 这其实也是一个研究问题,就是如何判断图的形态。

要在计算机中处理图,请抽象出表示下图的API。

Graph的API的实现可以用多种不同的数据结构表示,最基本的是维持一系列边的集合。

也可以使用邻接矩阵表示,如下所示:

也可以使用相邻列表表示如下:

这样的话,灵活性很高,如果用邻接列表表示的话,就可以定义以下的数据结构来表示Graph对象。

公共类图形

{private readonly int verticals; //顶点数

私有输入边缘; //边数

私有列表[ ]调整; //顶点连接列表

公共图形(int vertical ) )。

{this.verticals=vertical; this.edges=0;

adjacency=new List[vertical]; for(intv=0; v vertical; v )

{

adjacency[v]=new List (;

}

}公共集成电路() )

{returnverticals;

}public intGetEdges () )

{返回边缘;

} publicvoidaddedge (intverticalstart,intverticalEnd ) )。

{

adjacency [ vertical start ].add (vertical end;

adjacency [ vertical end ].add (vertical start;

edges;

} publiclistgetadjacency (intv etical ) )。

{returnadjacency[vetical];

}

}

view代码

采用上述三种表现方式的效率如下。

在研究了图的表示之后,让我们来看看图中重要的算法——深度优先算法。

深度优先算法

在谈深度优先算法之前,让我们先看一下迷宫搜索问题。 迷宫与图的对应关系如下所示。 迷宫中的每个交点表示图的顶点,每个通道对应一条边。 迷宫搜索可以采用Trmaux绳索搜索法。 也就是说:

把绳子放在后面

在所有访问过的地方放置绳子标志访问过的十字路口和通道

遇到访问过的地方,沿着绳子回到以前没有访问过的地方:

该图标如下所示:

下面是迷宫探索的小视频:

深度优先搜索算法模拟迷宫搜索。 实际的图表处理算法通常将图表的显示与图表的处理逻辑分开。 算法的总体设计模式如下。

创建Graph对象

将Graph对象传递给图形算法处理对象,如Paths对象

查询处理后的结果以获取信息

以下是深度优先的基本代码,可以看到递归调用dfs方法,以在调用之前确定是否访问了该节点。

publicclassdepthfirstsearch { private boolean [ ]标记; //marked[v]=is there an s-v path?

私有计数; //number of vertices connected to s

publicdepthfirstsearch(graphg,ints ) {

marked=newBoolean[g.v(] () ];

DFS(g,s );

/p>

}//depth first search from v

private void dfs(Graph G, intv) {

count++;

marked[v]= true;for (intw : G.adj(v)) {if (!marked[w]) {

dfs(G, w);

}

}

}public boolean marked(intv) {returnmarked[v];

}public intcount() {returncount;

}

}

View Code

试验一个算法最简单的办法是找一个简单的例子来实现。


深度优先路径查询

有了这个基础,我们可以实现基于深度优先的路径查询,要实现路径查询,我们必须定义一个变量来记录所探索到的路径。所以在上面的基础上定义一个edgesTo变量来后向记录所有到s的顶点的记录,和仅记录从当前节点到起始节点不同,我们记录图中的每一个节点到开始节点的路径。为了完成这一日任务,通过设置edgesTo[w]=v,我们记录从v到w的边,换句话说,v-w是最后一条从s到达w的边。edgesTo[]其实是一个指向其父节点的树。



public classDepthFirstPaths

{private bool[] marked;//记录是否被dfs访问过

private int[] edgesTo;//记录最后一个到当前节点的顶点

private int s;//搜索的起始点

public DepthFirstPaths(Graph g, ints)

{

marked= newbool[g.GetVerticals()];

edgesTo= new int[g.GetVerticals()];this.s =s;

dfs(g, s);

}private void dfs(Graph g, intv)

{

marked[v]= true;

foreach (intw in g.GetAdjacency(v))

{if (!marked[w])

{

edgesTo[w]=v;

dfs(g,w);

}

}

}public bool HasPathTo(intv)

{returnmarked[v];

}public Stack PathTo(intv)

{if (!HasPathTo(v)) return null;

Stack path = new Stack();for (int x = v; x!=s; x=edgesTo[x])

{

path.Push(x);

}

path.Push(s);returnpath;

}

}

View Code


上图中是黑色线条表示深度优先搜索中,所有定点到原点0的路径,他是通过edgeTo[]这个变量记录的,可以从右边可以看出,他其实是一颗树,树根即是原点,每个子节点到树根的路径即是从原点到该子节点的路径。下图是深度优先搜索算法的一个简单例子的追踪。


广度优先算法

通常我们更关注的是一类单源最短路径的问题,那就是给定一个图和一个源S,是否存在一条从s到给定顶点v的路径,如果存在,找出最短的那条(这里最短定义为边的条数最小)。深度优先算法是将未被访问的节点放到一个栈中(stack),虽然在上面的代码中没有明确在代码中写stack,但是递归间接的利用递归堆实现了这一原理。和深度优先算法不同,广度优先是将所有未被访问的节点放到了队列中。其主要原理是:

将s放到FIFO中,并且将s标记为已访问

重复直到队列为空

移除最近最近添加的顶点v

将v未被访问的邻接点添加到队列中

标记他们为已经访问

广度优先是以距离递增的方式来搜索路径的。



classBreadthFirstSearch

{privatebool[] marked;private int[] edgeTo;private int sourceVetical;//Source vertical

public BreadthFirstSearch(Graph g, ints)

{

marked=newbool[g.GetVerticals()];

edgeTo=new int[g.GetVerticals()];this.sourceVetical =s;

bfs(g, s);

}private void bfs(Graph g, ints)

{

Queue queue = new Queue();

marked[s]= true;

queue.Enqueue(s);while (queue.Count()!=0)

{int v =queue.Dequeue();

foreach (intw in g.GetAdjacency(v))

{if (!marked[w])

{

edgeTo[w]=v;

marked[w]= true;

queue.Enqueue(w);

}

}

}

}public bool HasPathTo(intv)

{returnmarked[v];

}public Stack PathTo(intv)

{if (!HasPathTo(v)) return null;

Stack path = new Stack();for (int x = v; x!=sourceVetical; x=edgeTo[x])

{

path.Push(x);

}

path.Push(sourceVetical);returnpath;

}

}

View Code

广度优先算法的搜索步骤如下:


广度优先搜索首先是在距离起始点为1的范围内的所有邻接点中查找有没有到达目标结点的对象,如果没有,继续前进在距离起始点为2的范围内查找,依次向前推进。


连通分量

使用深度优先遍历计算图的所有连通分量。



packageGraph;public classCC {private boolean[] marked;private int[] id;private intcount;public CC(Graph graph, ints) {

marked= new boolean[graph.V()];

id= new int[graph.V()];for (int i =0; i < graph.V(); i++) {if (!marked(i)) {

dfs(graph, i);

count++;

}

}

}private void dfs(Graph graph, ints) {

marked[s]= true;

id[s]=count;for (intW : graph.adj(s)) {if(marked(W))

dfs(graph, W);

}

}//判断v和W是否连通

public boolean connected(int v, intw) {return id[v] ==id[w];

}//返回W所在的连通分量的标识符

public int id(intv) {returnid[v];

}public boolean marked(intw) {returnmarked[w];

}//连通分量

public intcount() {returncount;

}

}

View Code

总结

本文简要介绍了无向图中的深度优先和广度优先算法,这两种算法时图处理算法中的最基础算法,也是后续更复杂算法的基础。其中图的表示,图算法与表示的分离这种思想在后续的算法介绍中会一直沿用,下文将讲解无向图中深度优先和广度优先的应用,以及利用这两种基本算法解决实际问题的应用。


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐