圆图的数据结构与算法

JSS*_*JSS 2 java algorithm tree

我有个要求,定义Data StructureAlgorithm用于Circular Data Graph为网络客户端.
在服务器上,数据将以2列CSV格式提供(例如发送者,接收者).
最终输出将以JSON格式呈现并发送到Web请求.
我见过一些Tree可以帮助父子关系的例子.但在我的情况下,我有一个递归的关系i.e. A Parent's grand child can also be used as a Parent; 当我遇到无限循环时,这会让生活变得困难.

数据:

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
Run Code Online (Sandbox Code Playgroud)

客户端可以像这样呈现(我只关心Java结构):
客户端可以请求任何节点,我必须生成整个树并发送响应,即A,K或N.

在此输入图像描述

问题:

  1. Data Structure这个要求最好的是什么?例如Tree喜欢还是其他?
  2. 我应该编写自己的逻辑来读取数据并设置Tree或是否有任何标准算法?
  3. 什么是避免递归的最佳方法?

任何工作的例子都会有帮助:)

另请参阅下面的工作解决方案.

Mal*_*ith 6

我确定你已经发现的树例子在如何实现树状结构方面是正确的.在您的情况下,您有一个额外的复杂性,即递归循环可能存在,因为某些子项将是对其祖先之一的精确对象引用.(对?)

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

在这种情况下,您不再真正处理树.在数学上,树被定义为没有循环.在您的情况下,您有周期,因此不是树而是圆形图.

我过去曾经处理过这种情况,我想你可以用两种方式处理这个问题.

  1. 打破周期(在对象级别),返回到树.在发生这些递归连接之一的情况下,不要将真实对象引用放置到祖先,而是指示它连接到哪个对象而不是该项的对象引用的存根.

  2. 接受您有一个圆形图,并确保您的代码在遍历图表时可以应对此问题.确保与图形交互的任何客户端代码都可以检测它何时处于递归分支中并适当地处理它.

恕我直言选项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;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)