2

Webクライアント用に定義する必要がData Structureあります。 サーバーでは、データは2列のCSV形式(送信者、受信者など)で提供されます。 最終出力はフォーマットでレンダリングされ、Webリクエストに送信されます。親子関係に役立つ いくつかの例を見てきました。しかし、私の場合、私には再帰的な関係があります。無限ループに陥ると、人生が少し難しくなります。AlgorithmCircular Data Graph

JSON
Treei.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

クライアントはこのようにレンダリングできます(私はJava構造のみを気にします):
クライアントは任意のノードを要求でき、ツリー全体を生成して応答、つまりA、K、またはNを送信する必要があります。

ここに画像の説明を入力してください

質問:

  1. Data Structureこの要件に最適なものは何ですか?たとえばTree、他のようなものですか?
  2. データを読み取って設定するための独自のロジックを作成するTree必要がありますか、それとも標準的なアルゴリズムがありますか?
  3. 再帰を回避するための最良の方法は何ですか?

実用的な例はここで本当に役立ちます:)

以下の私の実用的な解決策も参照してください。

4

4 に答える 4

6

あなたがすでに見つけた木の例は、構造のような木の実装方法について正しいと確信しています。あなたの場合、特定の子がそれらの祖先の1つへの正確なオブジェクト参照であるため、再帰ループが存在する可能性があるという追加の複雑さがあります。(右?)

この複雑さのために、各ノードの子を反復処理することによってツリーをトラバースしようとするプロセスは、スタックオーバーフローが発生するまで、これらの再帰的な接続をループします。

この場合、あなたはもはや実際に木を扱っていません。数学的には、ツリーはサイクルのないグラフとして定義されます。あなたの場合、サイクルがあり、したがってツリーではなく円グラフがあります

私は過去にそのような状況に対処したことがありますが、これには2つの方法で対処できると思います。

  1. サイクルを(オブジェクトレベルで)中断して、ツリーに戻ります。これらの再帰的な接続の1つが発生する場合は、実際のオブジェクト参照を祖先に配置するのではなく、そのアイテムへのオブジェクト参照ではなく、接続するオブジェクトを示すスタブを配置します。

  2. 円グラフがあることを受け入れ、グラフをトラバースするときにコードがこれに対処できることを確認します。グラフと相互作用するクライアントコードが、グラフが再帰ブランチにあることを検出し、適切に処理できることを確認してください。

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;
        }
    }

}
于 2012-06-03T22:04:39.110 に答える
1

あなたの例をメモリに保存しますか?あなたが説明しているのはグラフなので、これを見てください:グラフをメモリに保存する3つの方法、長所と短所

于 2012-06-03T21:43:17.507 に答える
0

私自身の解決策:

public class MyTree 
{
    static Set<String> fileDataList = new HashSet<String>();
    static
    {
        fileDataList.add("A,B");
        fileDataList.add("A,B");
        fileDataList.add("A,H");
        fileDataList.add("B,C");
        fileDataList.add("B,D");
        fileDataList.add("D,E");
        fileDataList.add("E,F");
        fileDataList.add("F,G");
        fileDataList.add("G,C");
        fileDataList.add("H,I");
        fileDataList.add("H,J");
        fileDataList.add("J,K");
        fileDataList.add("K,L");
        fileDataList.add("L,M");
        fileDataList.add("M,K");
        fileDataList.add("L,N");
        fileDataList.add("N,O");
        fileDataList.add("N,Q");
        fileDataList.add("O,P");
        fileDataList.add("P,A");
    }

    static Map<String, Set<String>> dataMap =new HashMap<String, Set<String>>();
    static Map<String, Set<Node>> nodeMap =new HashMap<String, Set<Node>>();

    public static void main(String args[]) throws IOException
    {
        //String fileName= "data.csv";
        //fileDataList = JSSTest.getFileData(fileName);        
        //String lineageFor="100";
        String lineageFor="A";

        //Build map
        for(String kv : fileDataList)
        {
            String arr[] = kv.split(",");
            String p = arr[0];
            String c = arr[1];
            if(dataMap.containsKey(p))
            {
                dataMap.get(p).add(c);
            }
            else
            {
                Set<String> list = new HashSet<String>();
                list.add(c);
                dataMap.put(p, list);
            }
        }

        //Get the lineage for Application
        Node lineage = getLineage(lineageFor);
        print(lineage, "");
    }

    private static void print(Node node, String space) 
    {
        System.out.println(space + node.getName());

        if(node.getInNode() != null && node.getInNode().size() > 0)
        {
            for(Node child:node.getInNode())
            {
                if(child.getRecursive() == null)
                {
                    print(child, space+".");
                }
                else
                {
                    System.out.println(space + child.getName());
                    System.out.println(space  + "."+ child.getRecursive().getName());
                }
            }
        }
    }

    /**
     * Get Lineage
     * @param appName
     * @return
     */
    private static Node getLineage(String appName) 
    {
        Node node = new Node(appName);
        Set<String> allInNodes = new HashSet<String>();
        setInnerNodes(node, dataMap.get(appName), allInNodes);
        return node;
    }

    private static void setInnerNodes(Node node, Set<String> childrenList, Set<String> allInNodes) 
    {
        if(childrenList == null) return;

        for(String child:childrenList)
        {
            Node containedNode = nodeContains(node, child);
            if(containedNode != null)
            {
                node.setRecursive(new Node(child));
                continue;
            }

            Node childNode = new Node(child);            
            node.addNode(childNode);
            if(dataMap.containsKey(child))
            {
                setInnerNodes(childNode, dataMap.get(child), allInNodes);
            }
        }
    }

    static int nodeCounter=1;
    private static Node nodeContains(Node node, String child) 
    {
        if(node.getName().equals(child)) return node;

        if(node.getParent() != null)
        {
            if(node.getParent().getName().equals(child)) return node.getParent();

            if(node.getParent().getParent() != null && nodeContains(node.getParent().getParent(), child) != null)
            {
                return node.getParent();
            }
        }

        return null;
    }
}
class Node
{
    private String name;
    private Node parent;
    private Node recursive;

    public Node getParent() 
    {
        return parent;
    }

    public void setParent(Node parent) {
        this.parent = parent;
    }

    private Set<Node> inNode = new LinkedHashSet<Node>();

    public Node(String name) 
    {
        this.name=name;
    }

    public void addNode(Node child)
    {
        child.parent=this;
        this.inNode.add(child);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Node> getInNode() {
        return inNode;
    }

    public void setInNode(Set<Node> inNode) {
        this.inNode = inNode;
    }

    public Node getRecursive() {
        return recursive;
    }

    public void setRecursive(Node recursive) {
        this.recursive = recursive;
    }
}
于 2012-06-08T16:42:12.637 に答える
-1

再帰を使用し、各出力要素の前にいくつのスペースがあるかを示す深度パラメーターを提供します。

次のようなもの:

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(" ");
    }
}

もちろん、getChildren()自分で実装する必要があります。あなたが地図や木を持っているなら、それほど難しいことではないはずです。

ただし、この例では循環参照に対応していないため、永久ループになります。

于 2012-06-03T21:21:04.643 に答える