2

任意のグラフで tarjans アルゴリズムを実行すると、次のグラフのように常にサイクルがあると主張されます。

A -> B -> C

アルゴリズムは、サイクルがあることを教えてくれます。

[a]
[b]

たとえば、次のようなサイクルがある場合:

A -> B -> C -> A

出力は非常に奇妙です:

[c, b, a]
[a]
[b]

これが私の実装です:

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.stream.Collectors;

public class Tarjans {

    private static class Node {
        public int index = -1, lowLink = -1;
        public String name;

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

        public String toString() {
            return name;
        }
    }

    HashMap<String, Node> nodes = new HashMap<>();
    HashMap<String, ArrayList<Node>> graph = new HashMap<>();

    private int index = 0;
    private ArrayDeque<Node> visited = new ArrayDeque<>();
    private HashSet<String> stack = new HashSet<>();

    public ArrayList<ArrayList<Node>> tarjan() {
        ArrayList<ArrayList<Node>> cycles = new ArrayList<>();
        for (String key : graph.keySet()) {
            Node n = nodes.get(key);
            if (n == null) {
                System.err.println("what is " + n + "?");
                return new ArrayList<ArrayList<Node>>();
            }

            ArrayList<Node> cycle = strongConnect(n);
            if (cycle.size() > 0) {
                cycles.add(cycle);
            }
        }
        return cycles;
    }

    private ArrayList<Node> strongConnect(Node node) {
        node.index = index;
        node.lowLink = index;
        index += 1;

        visited.push(node);
        stack.add(node.name);

        ArrayList<Node> neighbours = graph.get(node.name);
        if (neighbours == null) return new ArrayList<>();

        neighbours.forEach(n -> {
            if (n.index == -1) {
                strongConnect(n);
                node.lowLink = Math.min(node.lowLink, n.lowLink);
            }
            else if (stack.contains(n.name)) {
                node.lowLink = Math.min(node.lowLink, n.index);
            }
        });

        ArrayList<Node> cycle = new ArrayList<>();
        if (node.lowLink == node.index) {
            Node p = null;
            do {
                p = visited.pop();
                stack.remove(p.name);
                cycle.add(p);
            } while (p != node);
        }
        return cycle;
    }

    private void foo() {
        nodes.put("a", new Node("a"));
        nodes.put("b", new Node("b"));
        nodes.put("c", new Node("c"));

        // A -> B -> C -> A
        graph.put("a", new ArrayList<>(Arrays.asList(nodes.get("b"))));
        graph.put("b", new ArrayList<>(Arrays.asList(nodes.get("c"))));
        graph.put("c", new ArrayList<>(Arrays.asList(nodes.get("a"))));

        ArrayList<ArrayList<Node>> cycles = tarjan();
        for (ArrayList<Node> cycle : cycles) {
            System.out.println("[" + cycle.stream().map(Node::toString).collect(Collectors.joining(",")) + "]");
        }
    }

    public static void main(String[] args) {
        new Tarjans().foo();
    }

}

しかし、どこが間違っているのかわかりません。私はほぼ1:1のtarjansアルゴリズムと疑似コードに関するウィキペディアの記事に従いました。私はグラフ理論とグラフアルゴリズムに非常に慣れていないので、ここで何が間違いなのか頭を悩ませることはできません。

tarjan() の修正

public ArrayList<ArrayList<Node>> tarjan() {
    ArrayList<ArrayList<Node>> cycles = new ArrayList<>();
    for (Node n : nodes.values()) {
        if (n == null) {
            System.err.println("what is " + n + "?");
            return new ArrayList<ArrayList<Node>>();
        }

        if (n.index == -1) {
            ArrayList<Node> cycle = strongConnect(n);
            if (cycle.size() > 0) {
                cycles.add(cycle);
            }   
        }
    }
    return cycles;
}
4

1 に答える 1

1

質問で提示されたコードの最初のリビジョンから、問題はnearly十分に近くないことに要約されます: I've followed the wikipedia article on [Tarjan's Strongly Connected Components] algorithm nearly 1:1 and the pseudocode.
(そして、おそらく (保持する変数)強連結成分 cycleの命名: 辺 (a, b), (a, c), (b, a), (b, c) および (c, a) が 1 つのグラフに属している場合、頂点/ノード a、b、および c は、循環でも (対ごとに) 頂点を共有する循環でもない1 つの強く接続されたコンポーネント内にあります。)

strongConnect()すでに訪問済みのノードが呼び出されていました - リビジョン 7 で修正
ました。いったん見つかった強連結成分 の処理は、可能な限り簡単ではありません。「アルゴリズム(インスタンス)」のデータメンバーとして、それを追加するだけです。
Set<Set<Node>>

実装が機能し、コードがクリーンアップされ、コメントが付けられたら、CODE REVIEWで発表することをお勧めします。(Java の) コーダーとして、すべての人の生活を楽にする機会がたくさんあります。

于 2016-11-22T10:18:13.530 に答える