`
aty
  • 浏览: 36513 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

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

阅读更多

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

 

有向图的实现如下:

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<T> implements Iterable<T> 
{

    private final Map<T, Set<T>> mGraph = new HashMap<T, Set<T>>();
	
    /**
     * 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<T>());
        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<T> edgesFrom(T node) {
        /* Check that the node exists. */
        Set<T> 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<T> 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<T>
{
    private static final String marked = "marked";

    private static final String complete = "complete";

    private DirectedGraph<T> graph;

    private Map<T, String> marks;

    private List<T> verticesInCycles;

    public CycleDetector(DirectedGraph<T> graph)
    {
        this.graph = graph;
        marks = new HashMap<T, String>();
        verticesInCycles = new ArrayList<T>();
    }

    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<T> localCycles = new ArrayList<T>();

        // 当前顶点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<T> getVerticesInCycles()
    {
        return verticesInCycles;
    }
}

 

0
1
分享到:
评论

相关推荐

    回文判断 JAVA实现

    1. **Java基础**:首先,我们需要了解Java的基本语法,包括变量声明、条件语句(如if-else)、循环结构(如for和while)、方法定义以及类的创建。这些是编写任何Java程序的基础。 2. **字符串操作**:在回文判断中...

    java实现强连通图

    强连通图是指有向图中任意两个顶点都相互可达的图。也就是说,如果存在一条从顶点A到顶点B的路径,同时也存在一条从顶点B回顶点A的路径,那么这两个顶点是强连通的。如果图中所有顶点都满足此条件,该图则被称为强...

    JAVA循环 练习题

    - **实现思路**:使用循环遍历数组,对于每个数,检查是否存在另一个数满足条件。 #### 29. 二维数组对角线元素求和 - **题目解析**:对于一个3*3的矩阵,求对角线元素的和。 - **实现思路**:使用双重for循环,...

    Java判断无向图中是否存在环

    总结起来,这个Java程序通过深度优先搜索算法解决了无向图中环路检测的问题,利用了邻接表数据结构以及记录父节点的方法来判断是否存在环。这种方法简洁且直观,是图论和算法学习中的一个经典实例。

    用·java实现yolo算法,训练自己的数据 由浅入深代码集.docx

    - 目标分类:`classify()`函数基于提取的特征对每个块进行分类,判断是否存在目标。 - 定位目标:`retrieveTargetRect()`函数根据分类结果确定目标位置,生成边界框矩形`Rect`。 3. **后处理**: - 结果合并:将...

    java实现《围猫》游戏,基于广度优先算法

    6. **循环与判断**:在BFS搜索和猫咪移动的过程中,不断循环,直到游戏结束。在每次循环时,检查游戏状态,判断是否结束。 7. **用户交互**:为了增加游戏趣味性,可以添加用户交互功能,让用户决定下一步移动,...

    Java实现文件下载功能

    通过上述步骤和代码示例,我们可以清晰地了解到如何使用Java实现文件下载功能。这种能力对于构建功能丰富的Web应用程序至关重要,尤其是在需要提供文档、图片、视频等多媒体资源下载的情况下。掌握这些技术细节,将...

    拓扑排序java实现

    - 使用循环和条件判断确保输入的数据符合拓扑排序的要求:不存在自环且没有重复的边。 ##### 4. 排序方法 sort 该方法实现了拓扑排序的核心逻辑: - 首先遍历所有的边并将它们添加到 `HashSet array` 中,以便去除...

    java实现的俄罗斯方块源代码

    在Java中实现俄罗斯方块,首先需要一个游戏循环来处理游戏的更新和渲染。游戏循环通常包括三个主要部分:更新(Update)、渲染(Render)和输入处理(Input Handling)。在Java中,我们可以使用JavaFX或Swing库创建...

    JAVA实现的Caesar加密算法

    在JAVA中实现Caesar加密算法,可以深入了解字符编码、字符串处理和循环结构等编程基础知识。 首先,我们需要了解Java的基础语法,包括类、对象和方法的定义。在Java中,我们可以创建一个名为`CaesarCipher`的类,...

    利用循环队列实现AOV 网的拓扑排序

    AOV网(Activity On Vertex 网)是一种特殊的有向图,用于描述活动之间的相互关系。拓扑排序是AOV网中的一种基本操作,用于将网中的顶点排序,使得每个顶点的入度值为0。拓扑排序是一种非常重要的算法,它广泛应用于...

    基于Java实现的圣诞树

    本项目"基于Java实现的圣诞树"旨在利用Java语言的特性,展示如何通过代码生成一个可视化的圣诞树图形,这通常是编程学习过程中的一个经典练习,用于提升对控制流、循环和字符串操作的理解。 在Java中,我们可以通过...

    ll(1)判别

    为了解决这个问题,我们通常会创建一个析取函数(First集合)和一个跟随函数(Follow集合),来帮助判断是否存在冲突。 First集合表示一个非终结符可以开始的所有可能的终结符序列。对于一个非终结符A,First(A)...

    俄罗斯方块的Java实现

    在Java中实现俄罗斯方块,首先需要理解游戏的基本逻辑和规则。以下是一些关键知识点: 1. **图形用户界面(GUI)**:Java提供了丰富的GUI库,如JavaFX和Swing,用于构建游戏界面。你可以使用这些库创建一个包含游戏...

    Java开发技术大全(500个源代码).

    HelloNativeTest.java 测试本地化是否成功的类文件 instanceVar.java 定义一个实例成员变量 invokeByObject.java 对象实参传递示例程序 invokeByValue.java 传值调用示例程序 invokeMethod.java 同一个类中调用...

    Java 实现拓扑排序算法(源代码)

    其基本思想是对于有向图中的每条边 (u, v),确保顶点 u 在排序序列中位于顶点 v 之前。这种排序方式常被用于处理有先后顺序依赖的任务安排问题中,例如课程选修、项目依赖等场景。 #### 拓扑排序的实现步骤 拓扑...

    java俄罗斯方块实现

    Java实现的俄罗斯方块是一款经典的桌面游戏,它利用了编程语言的强大功能来模拟游戏的逻辑。在这个项目中,开发者使用Java编程语言,结合外部的多媒体库(可能是用于处理图形和声音),来创建了一个完整的可玩版本。...

    java局域网对战版五子棋

    6. **游戏逻辑**:五子棋的逻辑包括合法走法判断(不能在已有棋子的位置落子,不能走出棋盘范围)、胜负检测(连续五个相同颜色的棋子横向、纵向或斜向连成一线为胜)等,这些都是用Java算法实现的。 7. **事件监听...

    java笔记 java笔记

    ### Java基础知识概述 #### 1. 前言 Java是一种广泛使用的面向对象的编程语言,因其跨平台性、安全性和强大的功能而受到欢迎。...掌握这些核心概念和技术将有助于开发者更好地利用Java来解决问题。

    JAVA贪吃蛇源代码

    这通常是一个无限循环,不断地检查游戏状态,更新蛇的位置,判断是否吃到食物,以及检查蛇是否撞到边界或自身。在Java中,我们可以使用`while(true)`循环来实现这一点,并通过`Thread.sleep()`方法控制游戏的帧率,...

Global site tag (gtag.js) - Google Analytics