Java 圆图的数据结构与算法

Java 圆图的数据结构与算法,java,algorithm,tree,Java,Algorithm,Tree,我需要为web客户端的循环数据图定义数据结构和算法 在服务器上,数据将以2列CSV格式提供(例如发送方、接收方)。 最终输出将以JSON格式呈现,并发送到web请求。 我看到了一些树示例,它们有助于处理亲子关系。但在我的例子中,我有一个递归关系,即父母的孙子也可以用作父母;这让我的生活有点困难,因为我跑进了无限循环 数据: Sender,Receiver A,B A,H B,C B,D D,E E,F F,G G,C H,I H,J J,K K,L L,M M,K L,N N,O N,P P,A

我需要为web客户端的
循环数据图定义
数据结构
算法

在服务器上,数据将以2列CSV格式提供(例如发送方、接收方)。
最终输出将以
JSON
格式呈现,并发送到web请求。
我看到了一些
示例,它们有助于处理亲子关系。但在我的例子中,我有一个递归关系
,即父母的孙子也可以用作父母
;这让我的生活有点困难,因为我跑进了无限循环

数据:

Sender,Receiver
A,B
A,H
B,C
B,D
D,E
E,F
F,G
G,C
H,I
H,J
J,K
K,L
L,M
M,K
L,N
N,O
N,P
P,A
N,Q
客户端可能会这样呈现(我只关心Java结构):
客户端可以请求任何节点,我必须生成整个树并发送响应,即A、K或N

问题:

Sender,Receiver
A,B
A,H
B,C
B,D
D,E
E,F
F,G
G,C
H,I
H,J
J,K
K,L
L,M
M,K
L,N
N,O
N,P
P,A
N,Q
  • 对于此要求,最佳的
    数据结构是什么?对于
    示例
    类似还是其他
  • 我应该写我自己的逻辑来阅读吗 数据和设置在
    树中
    或是否有任何标准算法 在那里
  • 避免递归的最佳方法是什么
  • 在这里,任何工作示例都非常有用:)


    请参阅下面我的工作解决方案。

    使用递归并提供一个深度参数,该参数告诉每个输出元素前面有多少空格

    大致如下:

    private final int INDENTSIZE = 4;
    
    public void printNodes() {
        for (Node n : roots) {
            System.out.print("- " + n.getName());
            printNode(n, 1);
        }
    }
    
    private void printNode(Node n, int depth) {
        List<Node> children = n.getChildren();
    
        for (Node child : children) {
            printIndent(depth);
            System.out.print("- " + child.getName());
            printNode(child, depth + 1);
        }
    }
    
    private void printIndent(int depth) {
        System.out.println();
        for (int i = 0; i < depth * INDENTSIZE; i++) {
            System.out.print(" ");
        }
    }
    
    private final int INDENTSIZE=4;
    公共void printNodes(){
    用于(节点n:根){
    System.out.print(“-”+n.getName());
    printNode(n,1);
    }
    }
    私有void printNode(节点n,int depth){
    List children=n.getChildren();
    用于(节点子节点:子节点){
    打印缩进(深度);
    System.out.print(“-”+child.getName());
    printNode(子节点,深度+1);
    }
    }
    专用void打印缩进(整数深度){
    System.out.println();
    对于(int i=0;i
    当然,您必须自己实现
    getChildren()
    。如果你有一张地图或一棵树,应该不会那么难


    然而,这个示例并不处理循环引用,然后将永远循环。

    是否将您的示例存储在内存中?由于您所描述的是一个图形,请看一下:

    我确信您已经找到的树示例对于如何实现树状结构是正确的。在您的例子中,递归循环可能存在,因为某些子循环将是对其祖先的精确对象引用,这增加了复杂性。(对吗?)

    由于这种复杂性,任何试图通过迭代每个节点的子节点来遍历树的进程都将围绕这些递归连接循环,直到发生堆栈溢出

    在这种情况下,您不再真正与树打交道。从数学上讲,树被定义为树。就你而言,你拥有的不是一棵树,而是一棵树

    我过去曾处理过这种情况,我认为你可以通过两种方式来处理

  • 打断循环(在对象级别),以返回到树。在发生这些递归连接之一的情况下,不要将实际对象引用放在祖先上,而是放在一个存根上,该存根指示它连接到哪个对象,而不是该项的对象引用

  • 接受您有一个循环图,并确保您的代码在遍历该图时能够处理此问题。确保与图形交互的任何客户端代码都可以检测到它何时处于递归分支中,并对其进行适当处理

  • IMHO选项2不是很有吸引力,因为您可能会发现很难保证约束,而且它经常会导致错误。只要您可以为树中的每个项目分配一个唯一的标识符,选项1就可以很好地工作,尽管客户端仍然需要意识到发生这种情况的可能性,以便能够处理解耦链接并正确地表示它(例如在树视图UI中)。您仍然希望对循环图建模,但将使用树在对象级别表示它,因为它简化了代码(和表示)

    选项1的完整示例:

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    
    
    public class CyclicGraphTest 
    {   
        public static void main(String[] args) 
        {       
            new CyclicGraphTest().test();           
        }
    
        public void test()
        {
            NodeManager manager = new NodeManager();
            Node root = manager.processNode("ZZZ");
            root.add(manager.processNode("AAA"));
            manager.get("AAA").add(manager.processNode("BBB"));
            manager.get("AAA").add(manager.processNode("CCC"));
            manager.get("AAA").add(manager.processNode("DDD")); 
            manager.get("DDD").add(manager.processNode("EEE"));
            manager.get("EEE").add(manager.processNode("FFF"));
            manager.get("FFF").add(manager.processNode("AAA"));
            manager.get("AAA").add(manager.processNode("JJJ")); 
            root.add(manager.processNode("EEE"));
            GraphWalker walker = new GraphWalker(manager, root, 1);
            System.out.println(walker.printGraph());        
        }
    
        /**
         * Basic Node
         */
        public class Node implements Iterable<Node>
        {
            private String id;
            private List<Node> children = new ArrayList<Node>();
    
            public Node(String id) {            
                this.id = id;
            }
    
            public boolean add(Node e) {
                return children.add(e);
            }
    
            public String getId() {
                return id;
            }
    
            @Override
            public Iterator<Node> iterator() {          
                return children.iterator();
            }           
        }
    
        /**
         * Cyclical Reference
         * 
         */
        public class ReferenceNode extends Node
        {
            private String refId;
    
            public ReferenceNode(String id, String refId) {
                super(id);
                this.refId = refId;
            }
    
            @Override
            public boolean add(Node e) {
                throw new UnsupportedOperationException("Cannot add children to a reference");
            }
    
            public String getRefId() {
                return refId;
            }           
        }   
    
        /**
         * Keeps track of all our nodes. Handles creating reference nodes for existing
         * nodes.
         */
        public class NodeManager
        {
            private Map<String, Node> map = new HashMap<String, Node>();
    
            public Node get(String key) {
                return map.get(key);
            }
    
            public Node processNode(String id)
            {
                Node node = null;
                if(map.containsKey(id))
                {
                    node = new ReferenceNode(getRefId(id), id);
                    map.put(node.getId(), node);                
                }
                else
                {
                    node = new Node(id);
                    map.put(id, node);
                }
                return node;
            }
    
            private String getRefId(String id) {
                int i = 0;
                String refId = null;
                while(map.containsKey(refId = id + "###" + i)) { i++; }
                return refId;
            }
    
            public Node resolve(ReferenceNode node) {
                return map.get(node.getRefId());
            }
        }
    
        /**
         * Walks a tree representing a cyclical graph down to a specified level of recursion
         */
        public class GraphWalker
        {
            private NodeManager manager;
            private Node root;
            private int maxRecursiveLevel;
    
            public GraphWalker(NodeManager manager, Node root, int recursiveLevel) {
                super();
                this.manager = manager;
                this.root = root;
                this.maxRecursiveLevel = recursiveLevel;
            }
    
            public String printGraph()
            {
                return printNode(root, 0, "   ").toString();
            }
    
            private StringBuilder printNode(Node node, int recursionDepth, String prefix) {
                Node resolvedNode = resolveNode(node, recursionDepth);
                if(resolvedNode != node) {
                    recursionDepth ++;
                    node = resolvedNode;
                }
                StringBuilder sb = new StringBuilder(node.getId());
                int i = 0;
                for(Node child : node)
                {
                    if(i != 0) sb.append("\n").append(prefix);
                    sb.append(" -> ").append(printNode(child, recursionDepth, prefix + "       "));             
                    i++;
                }
                return sb;
            }
    
            /**
             * Returns a resolved reference to another node for reference nodes when the 
             * recursion depth is less than the maximum allowed
             * @param node
             * @param recursionDepth
             * @return
             */
            private Node resolveNode(Node node, int recursionDepth) 
            {           
                return (node instanceof ReferenceNode) && (recursionDepth < maxRecursiveLevel) ? 
                        manager.resolve((ReferenceNode) node) : node;
            }
        }
    
    }
    
    import java.util.ArrayList;
    导入java.util.HashMap;
    导入java.util.Iterator;
    导入java.util.List;
    导入java.util.Map;
    公开课自行车考试
    {   
    公共静态void main(字符串[]args)
    {       
    新的CyclicGraphTest().test();
    }
    公开无效测试()
    {
    NodeManager管理器=新的NodeManager();
    节点根=manager.processNode(“ZZZ”);
    root.add(manager.processNode(“AAA”);
    manager.get(“AAA”).add(manager.processNode(“BBB”);
    manager.get(“AAA”).add(manager.processNode(“CCC”);
    manager.get(“AAA”).add(manager.processNode(“DDD”);
    manager.get(“DDD”).add(manager.processNode(“EEE”);
    manager.get(“EEE”).add(manager.processNode(“FFF”);
    manager.get(“FFF”).add(manager.processNode(“AAA”);
    manager.get(“AAA”).add(manager.processNode(“JJJ”);
    root.add(manager.processNode(“EEE”);
    GraphWalker=newgraphwalker(manager,root,1);
    System.out.println(walker.printGraph());
    }
    /**
    *基本节点
    */
    公共类节点实现了Iterable
    {
    私有字符串id;
    private List children=new ArrayList();
    公共节点(字符串id){
    this.id=id;
    }
    公共布尔加法(节点e){
    返回子女。添加(e);
    }
    公共字符串getId(){
    返回id;
    }
    @凌驾
    公共迭代器迭代器(){
    返回children.iterator();
    }           
    }
    /**
    *循环引用
    * 
    */
    公共类引用节点扩展节点
    {
    私有字符串refId;
    公共引用节点(字符串id,Str)