java遍历有向图_java实现有向图和判断是否存在循环

最新在看xwork的源代码,XmlConfigurationProvider这个类,用来实现解析xwork.xml配置文件。该类使用了有向图这种数据结构,来判断是否存在元素的循环包含。

有向图的实现如下:

import java.util.Collections;

import java.util.HashMap;

import java.util.HashSet;

import java.util.Iterator;

import java.util.Map;

import java.util.NoSuchElementException;

import java.util.Set;

public final class DirectedGraph implements Iterable

{

private final Map> mGraph = new HashMap>();

/**

* Adds a new node to the graph. If the node already exists, this function is a no-op.

*

* @param node

* The node to add.

* @return Whether or not the node was added.

*/

public boolean addNode(T node) {

/* If the node already exists, don't do anything. */

if (mGraph.containsKey(node))

return false;

/* Otherwise, add the node with an empty set of outgoing edges. */

mGraph.put(node, new HashSet());

return true;

}

/**

* Given a start node, and a destination, adds an arc from the start node to the destination. If an arc already exists, this operation is a no-op.

* If either endpoint does not exist in the graph, throws a NoSuchElementException.

*

* @param start

* The start node.

* @param dest

* The destination node.

* @throws NoSuchElementException

* If either the start or destination nodes do not exist.

*/

public void addEdge(T start, T dest) {

/* Confirm both endpoints exist. */

if (!mGraph.containsKey(start)) {

throw new NoSuchElementException("The start node does not exist in the graph.");

} else if (!mGraph.containsKey(dest)) {

throw new NoSuchElementException("The destination node does not exist in the graph.");

}

/* Add the edge. */

mGraph.get(start).add(dest);

}

/**

* Removes the edge from start to dest from the graph. If the edge does not exist, this operation is a no-op. If either endpoint does not exist,

* this throws a NoSuchElementException.

*

* @param start

* The start node.

* @param dest

* The destination node.

* @throws NoSuchElementException

* If either node is not in the graph.

*/

public void removeEdge(T start, T dest) {

/* Confirm both endpoints exist. */

if (!mGraph.containsKey(start)) {

throw new NoSuchElementException("The start node does not exist in the graph.");

} else if (!mGraph.containsKey(dest)) {

throw new NoSuchElementException("The destination node does not exist in the graph.");

}

mGraph.get(start).remove(dest);

}

/**

* Given two nodes in the graph, returns whether there is an edge from the first node to the second node. If either node does not exist in the

* graph, throws a NoSuchElementException.

*

* @param start

* The start node.

* @param end

* The destination node.

* @return Whether there is an edge from start to end.

* @throws NoSuchElementException

* If either endpoint does not exist.

*/

public boolean edgeExists(T start, T end) {

/* Confirm both endpoints exist. */

if (!mGraph.containsKey(start)) {

throw new NoSuchElementException("The start node does not exist in the graph.");

} else if (!mGraph.containsKey(end)) {

throw new NoSuchElementException("The end node does not exist in the graph.");

}

return mGraph.get(start).contains(end);

}

/**

* Given a node in the graph, returns an immutable view of the edges leaving that node as a set of endpoints.

*

* @param node

* The node whose edges should be queried.

* @return An immutable view of the edges leaving that node.

* @throws NoSuchElementException

* If the node does not exist.

*/

public Set edgesFrom(T node) {

/* Check that the node exists. */

Set arcs = mGraph.get(node);

if (arcs == null)

throw new NoSuchElementException("Source node does not exist.");

return Collections.unmodifiableSet(arcs);

}

/**

* Returns an iterator that can traverse the nodes in the graph.

*

* @return An iterator that traverses the nodes in the graph.

*/

public Iterator iterator() {

return mGraph.keySet().iterator();

}

/**

* Returns the number of nodes in the graph.

*

* @return The number of nodes in the graph.

*/

public int size() {

return mGraph.size();

}

/**

* Returns whether the graph is empty.

*

* @return Whether the graph is empty.

*/

public boolean isEmpty() {

return mGraph.isEmpty();

}

}

通过上述源码发现:xwork框架采用的是有向图的

邻接表方法进行存储的。

xwork使用CycleDetector用来判断有向图是否存在循环,实现如下:

public class CycleDetector

{

private static final String marked = "marked";

private static final String complete = "complete";

private DirectedGraph graph;

private Map marks;

private List verticesInCycles;

public CycleDetector(DirectedGraph graph)

{

this.graph = graph;

marks = new HashMap();

verticesInCycles = new ArrayList();

}

public boolean containsCycle()

{

for (T v : graph)

{

// 如果v正在遍历或者遍历完成,不需要进入mark(),因为mark是一个递归调用,使用的是深度优先搜索算法;

// 这是为了保证1个顶点只会遍历一次

if (!marks.containsKey(v))

{

if (mark(v))

{

// return true;

}

}

}

return !verticesInCycles.isEmpty();

}

//DFS算法,遍历顶点vertex

// @return 当前顶点是否在环上

private boolean mark(T vertex)

{

List localCycles = new ArrayList();

// 当前顶点vertex,遍历开始

marks.put(vertex, marked);

for (T u : graph.edgesFrom(vertex))

{

// u的遍历还没有结束,说明存在u->vertex的通路,也存在vertex->u的通路,形成了循环

if (marks.containsKey(u) && marks.get(u).equals(marked))

{

localCycles.add(vertex);

// return true;

}

else if (!marks.containsKey(u))

{

if (mark(u))

{

localCycles.add(vertex);

// return true;

}

}

}

// 当前顶点vertex,遍历完成

marks.put(vertex, complete);

verticesInCycles.addAll(localCycles);

return !localCycles.isEmpty();

}

public List getVerticesInCycles()

{

return verticesInCycles;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * (有向)的深度优先遍算法模板 */ package dsa; public abstract class DFS extends GraphTraverse { //变量 protected static int clock = 0;//遍过程中使用的计时钟 //构造方法 public DFS(Graph g) { super(g); } //深度优先遍算法 protected Object traverse(Vertex v, Object info) {//从顶点v出发,做深度优先查找 if (UNDISCOVERED != v.getStatus()) return null;//跳过已访问过的顶点(针对非连通) v.setDStamp(clock++); v.setStatus(DISCOVERED); visit(v, info);//访问当前顶点 for (Iterator it = v.outEdges(); it.hasNext();) {//检查与顶点v Edge e = (Edge)it.getNext();//通过边e = (v, u) Vertex u = (Vertex)e.getVPosInV(1).getElem();//相联的每一顶点u switch (u.getStatus()) {//根据u当前的不同状态,分别做相应处理 case UNDISCOVERED ://若u尚未被发现,则 e.setType(TREE);//e被归类为“树边” traverse(u, info);//从u出发,继续做深度优先查找 break; case DISCOVERED ://若u已经被发现,但对其访问尚未结束,则 e.setType(BACKWARD);//将e归类为“后向跨边” break; default ://VISITED,即对u的访问已经结束 if (u.getDStamp() < v.getDStamp())//若相对于v,u被发现得更早,则 e.setType(CROSS);//将e归类为“横跨边” else//否则 e.setType(FORWARD);//将e归类为“前向跨边” break; } }//至此,v的所有邻居都已访问结束,故 v.setFStamp(clock++); v.setStatus(VISITED);//将v标记为VISITED return null;//然后回溯 } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值