119

以下で説明するタスクを達成するために、最適な時間効率の良いアルゴリズムを決定しようとしています。

私はレコードのセットを持っています。このレコードのセットには、このセットのレコードのペアが互いにどのように接続するかを示す接続データがあります。これは基本的に無向グラフを表し、レコードが頂点、接続データがエッジになります。

セット内のすべてのレコードには接続情報があります (つまり、孤立したレコードは存在しません。セット内の各レコードは、セット内の 1 つ以上の他のレコードに接続されます)。

セットから任意の 2 つのレコードを選択し、選択したレコード間の単純なパスをすべて表示できるようにしたいと考えています。「単純なパス」とは、パスに繰り返しレコードがないパス (つまり、有限パスのみ) を意味します。

注: 選択された 2 つのレコードは常に異なります (つまり、開始頂点と終了頂点が同じになることはありません。サイクルはありません)。

例えば:

    次の記録があるとします。
        A、B、C、D、E

    以下は接続を表しています。
        (A,B),(A,C),(B,A),(B,D),(B,E),(B,F),(C,A),(C,E),
        (C,F),(D,B),(E,C),(E,F),(F,B),(F,C),(F,E)

        [ここで、(A,B) はレコード A がレコード B に接続することを意味します]

開始レコードとして B を選択し、終了レコードとして E を選択した場合、レコード B をレコード E に接続するレコード接続を通るすべての単純なパスを見つけたいと思うでしょう。

   B を E に接続するすべてのパス:
      B->E
      B->F->E
      B->F->C->E
      B->A->C->E
      B->A->C->F->E

これは一例です。実際には、数十万のレコードを含むセットがある場合があります。

4

16 に答える 16

119

これは、グラフの深さ優先検索で達成できるようです。深さ優先検索では、2 つのノード間のすべての非循環パスが検出されます。このアルゴリズムは非常に高速で、大きなグラフに対応できる必要があります (グラフのデータ構造はまばらなので、必要なだけのメモリしか使用しません)。

上で指定したグラフには、方向性 (B,E) のエッジが 1 つしかないことに気付きました。これはタイプミスですか、それとも本当に有向グラフですか? このソリューションは関係なく機能します。申し訳ありませんが、私は C でそれを行うことができませんでした。私はその分野が少し苦手です。とはいえ、この Java コードは問題なく翻訳できると思います。

Graph.java:

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

public class Graph {
    private Map<String, LinkedHashSet<String>> map = new HashMap();

    public void addEdge(String node1, String node2) {
        LinkedHashSet<String> adjacent = map.get(node1);
        if(adjacent==null) {
            adjacent = new LinkedHashSet();
            map.put(node1, adjacent);
        }
        adjacent.add(node2);
    }

    public void addTwoWayVertex(String node1, String node2) {
        addEdge(node1, node2);
        addEdge(node2, node1);
    }

    public boolean isConnected(String node1, String node2) {
        Set adjacent = map.get(node1);
        if(adjacent==null) {
            return false;
        }
        return adjacent.contains(node2);
    }

    public LinkedList<String> adjacentNodes(String last) {
        LinkedHashSet<String> adjacent = map.get(last);
        if(adjacent==null) {
            return new LinkedList();
        }
        return new LinkedList<String>(adjacent);
    }
}

検索.java:

import java.util.LinkedList;

public class Search {

    private static final String START = "B";
    private static final String END = "E";

    public static void main(String[] args) {
        // this graph is directional
        Graph graph = new Graph();
        graph.addEdge("A", "B");
        graph.addEdge("A", "C");
        graph.addEdge("B", "A");
        graph.addEdge("B", "D");
        graph.addEdge("B", "E"); // this is the only one-way connection
        graph.addEdge("B", "F");
        graph.addEdge("C", "A");
        graph.addEdge("C", "E");
        graph.addEdge("C", "F");
        graph.addEdge("D", "B");
        graph.addEdge("E", "C");
        graph.addEdge("E", "F");
        graph.addEdge("F", "B");
        graph.addEdge("F", "C");
        graph.addEdge("F", "E");
        LinkedList<String> visited = new LinkedList();
        visited.add(START);
        new Search().depthFirst(graph, visited);
    }

    private void depthFirst(Graph graph, LinkedList<String> visited) {
        LinkedList<String> nodes = graph.adjacentNodes(visited.getLast());
        // examine adjacent nodes
        for (String node : nodes) {
            if (visited.contains(node)) {
                continue;
            }
            if (node.equals(END)) {
                visited.add(node);
                printPath(visited);
                visited.removeLast();
                break;
            }
        }
        for (String node : nodes) {
            if (visited.contains(node) || node.equals(END)) {
                continue;
            }
            visited.addLast(node);
            depthFirst(graph, visited);
            visited.removeLast();
        }
    }

    private void printPath(LinkedList<String> visited) {
        for (String node : visited) {
            System.out.print(node);
            System.out.print(" ");
        }
        System.out.println();
    }
}

プログラム出力:

B E 
B A C E 
B A C F E 
B F E 
B F C E 
于 2008-09-12T07:25:43.867 に答える
24

米国国立標準技術研究所(NIST)のオンラインアルゴリズムおよびデータ構造辞書では、この問題を「すべての単純なパス」としてリストし、深さ優先探索を推奨しています。CLRSは、関連するアルゴリズムを提供します。

ペトリネットを使用した巧妙なテクニックはここにあります

于 2008-09-16T19:20:44.037 に答える
13

これが私が思いついた疑似コードです。これは特定の疑似コードの方言ではありませんが、従うのに十分単純なはずです。

誰でもこれを選びたいです。

  • [p] は、現在のパスを表す頂点のリストです。

  • [x] は基準を満たすパスのリストです

  • [s] はソース頂点です

  • [d] は目的の頂点です

  • [c] は現在の頂点です (PathFind ルーチンへの引数)

隣接する頂点を検索する効率的な方法があると仮定します (6 行目)。

     1 パスリスト [p]
     2 ListOfPathLists [x]
     3 頂点 [s]、[d]

     4 PathFind (頂点 [c] )
     5 リスト [p] の末尾に [c] を追加
     6 [c] に隣接する各頂点 [v] について
     7 [v] が [d] と等しい場合
     8 リスト [p] を [x] に保存
     9 Else [v] がリストにない場合 [p]
    10 PathFind([v])
    11 ネクスト・フォー
    12 [p] からテールを削除
    13 戻る
于 2008-09-12T07:48:29.787 に答える
8

この回答で与えられた既存の非再帰的 DFS 実装は壊れているように見えるので、実際に機能するものを提供させてください。

私はこれを Python で書きました。かなり読みやすく、実装の詳細が整理されているためです (また、generatorsyieldを実装するための便利なキーワードがあるため)。ただし、他の言語への移植はかなり簡単なはずです。

# a generator function to find all simple paths between two nodes in a
# graph, represented as a dictionary that maps nodes to their neighbors
def find_simple_paths(graph, start, end):
    visited = set()
    visited.add(start)

    nodestack = list()
    indexstack = list()
    current = start
    i = 0

    while True:
        # get a list of the neighbors of the current node
        neighbors = graph[current]

        # find the next unvisited neighbor of this node, if any
        while i < len(neighbors) and neighbors[i] in visited: i += 1

        if i >= len(neighbors):
            # we've reached the last neighbor of this node, backtrack
            visited.remove(current)
            if len(nodestack) < 1: break  # can't backtrack, stop!
            current = nodestack.pop()
            i = indexstack.pop()
        elif neighbors[i] == end:
            # yay, we found the target node! let the caller process the path
            yield nodestack + [current, end]
            i += 1
        else:
            # push current node and index onto stacks, switch to neighbor
            nodestack.append(current)
            indexstack.append(i+1)
            visited.add(neighbors[i])
            current = neighbors[i]
            i = 0

このコードは 2 つの並列スタックを維持します。1 つは現在のパスの以前のノードを含み、もう 1 つはノード スタック内の各ノードの現在の近隣インデックスを含みます (ノードをポップしたときに近隣ノードの反復処理を再開できるようにするため)。スタック)。(ノード、インデックス) ペアの 1 つのスタックを同様に使用することもできましたが、2 つのスタックの方法の方が読みやすく、おそらく他の言語のユーザーにとっては実装が簡単であると考えました。

このコードvisitedでは、現在のノードとスタック上のすべてのノードを常に含む別のセットも使用して、ノードが既に現在のパスの一部であるかどうかを効率的に確認できるようにします。スタックのような効率的なプッシュ/ポップ操作と効率的なメンバーシップ クエリの両方を提供する「順序付きセット」データ構造が言語にある場合は、それをノード スタックに使用して、個別のvisitedセットを取り除くことができます。

または、ノードにカスタムの可変クラス/構造を使用している場合は、各ノードにブール値フラグを格納して、現在の検索パスの一部としてアクセスされたかどうかを示すことができます。もちろん、この方法では、何らかの理由で同じグラフに対して 2 つの検索を並行して実行することはできません。

上記の関数がどのように機能するかを示すテストコードを次に示します。

# test graph:
#     ,---B---.
#     A   |   D
#     `---C---'
graph = {
    "A": ("B", "C"),
    "B": ("A", "C", "D"),
    "C": ("A", "B", "D"),
    "D": ("B", "C"),
}

# find paths from A to D
for path in find_simple_paths(graph, "A", "D"): print " -> ".join(path)

与えられたサンプル グラフでこのコードを実行すると、次の出力が生成されます。

A -> B -> C -> D
A -> B -> D
A -> C -> B -> D
A -> C -> D

この例のグラフは無向 (つまり、すべての辺が双方向) ですが、このアルゴリズムは任意の有向グラフでも機能することに注意してください。たとえば、 (の隣接リストからC -> B削除することによって) エッジを削除すると、3 番目のパス ( ) を除いて同じ出力が得られますが、これはもはや不可能です。BCA -> C -> B -> D


Ps。このような単純な検索アルゴリズム (およびこのスレッドで提供されている他のアルゴリズム) のパフォーマンスが非常に低いグラフを作成するのは簡単です。

たとえば、無向グラフで A から B へのすべてのパスを検索するタスクを考えます。この場合、開始ノード A には 2 つの隣接ノードがあります。ターゲット ノード B (A 以外の隣接ノードはありません) と、クリークの一部であるノード C です。 n +1 ノードの場合、次のようになります。

graph = {
    "A": ("B", "C"),
    "B": ("A"),
    "C": ("A", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "D": ("C", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "E": ("C", "D", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "F": ("C", "D", "E", "G", "H", "I", "J", "K", "L", "M", "N", "O"),
    "G": ("C", "D", "E", "F", "H", "I", "J", "K", "L", "M", "N", "O"),
    "H": ("C", "D", "E", "F", "G", "I", "J", "K", "L", "M", "N", "O"),
    "I": ("C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "O"),
    "J": ("C", "D", "E", "F", "G", "H", "I", "K", "L", "M", "N", "O"),
    "K": ("C", "D", "E", "F", "G", "H", "I", "J", "L", "M", "N", "O"),
    "L": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "M", "N", "O"),
    "M": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "N", "O"),
    "N": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "O"),
    "O": ("C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N"),
}

A と B の間の唯一のパスが直接パスであることは簡単にわかりますが、ノード A から開始されたナイーブな DFS は、クリーク内のパスを無駄に探索するために O( n !) 時間を無駄にします。これらのパスのいずれも B につながる可能性はありません。

同様のプロパティを持つDAGを構築することもできます。たとえば、開始ノード A をターゲット ノード B に接続し、他の 2 つのノード C 1および C 2に接続します。これらのノードは両方ともノード D 1および D 2に接続し、両方ともノード E に接続します。1と E 2など。このように配置されたn層のノードの場合、A から B へのすべてのパスを単純に検索すると、O(2 n ) の時間が無駄になり、あきらめる前にすべての行き止まりを調べることになります。

もちろん、(C 以外の) クリーク内のノードの 1 つから、または DAG の最後の層からターゲット ノード B にエッジを追加すると、A から B への指数関数的に多数の可能なパス作成されます。純粋にローカルな検索アルゴリズムでは、そのようなエッジが見つかるかどうかを事前に知ることはできません。したがって、ある意味では、このような素朴な検索の出力感度が低いのは、グラフのグローバル構造に対する認識の欠如によるものです。

これらの「指数時間のデッドエンド」のいくつかを回避するために使用できるさまざまな前処理方法(リーフノードを繰り返し削除する、単一ノードの頂点セパレーターを検索するなど)がありますが、一般的な方法は知りません。すべての場合にそれらを排除できる前処理トリック。一般的な解決策は、検索のすべてのステップでターゲット ノードがまだ到達可能かどうかを確認し (サブサーチを使用)、到達可能でない場合は早期にバックトラックすることですが、悲しいことに、検索が大幅に遅くなります (最悪の場合、 、グラフのサイズに比例する)などの病理学的行き止まりを含まない多くのグラフ。

于 2016-02-21T01:36:30.963 に答える
5

これは、2 階に比べて論理的に見栄えの良い再帰バージョンです。

public class Search {

private static final String START = "B";
private static final String END = "E";

public static void main(String[] args) {
    // this graph is directional
    Graph graph = new Graph();
    graph.addEdge("A", "B");
    graph.addEdge("A", "C");
    graph.addEdge("B", "A");
    graph.addEdge("B", "D");
    graph.addEdge("B", "E"); // this is the only one-way connection
    graph.addEdge("B", "F");
    graph.addEdge("C", "A");
    graph.addEdge("C", "E");
    graph.addEdge("C", "F");
    graph.addEdge("D", "B");
    graph.addEdge("E", "C");
    graph.addEdge("E", "F");
    graph.addEdge("F", "B");
    graph.addEdge("F", "C");
    graph.addEdge("F", "E");
    List<ArrayList<String>> paths = new ArrayList<ArrayList<String>>();
    String currentNode = START;
    List<String> visited = new ArrayList<String>();
    visited.add(START);
    new Search().findAllPaths(graph, seen, paths, currentNode);
    for(ArrayList<String> path : paths){
        for (String node : path) {
            System.out.print(node);
            System.out.print(" ");
        }
        System.out.println();
    }   
}

private void findAllPaths(Graph graph, List<String> visited, List<ArrayList<String>> paths, String currentNode) {        
    if (currentNode.equals(END)) { 
        paths.add(new ArrayList(Arrays.asList(visited.toArray())));
        return;
    }
    else {
        LinkedList<String> nodes = graph.adjacentNodes(currentNode);    
        for (String node : nodes) {
            if (visited.contains(node)) {
                continue;
            } 
            List<String> temp = new ArrayList<String>();
            temp.addAll(visited);
            temp.add(node);          
            findAllPaths(graph, temp, paths, node);
        }
    }
}
}

プログラム出力

B A C E 

B A C F E 

B E

B F C E

B F E 
于 2011-11-25T00:28:31.737 に答える
4

C コードでのソリューション。最小限のメモリを使用する DFS に基づいています。

#include <stdio.h>
#include <stdbool.h>

#define maxN    20  

struct  nodeLink
{

    char node1;
    char node2;

};

struct  stack
{   
    int sp;
    char    node[maxN];
};   

void    initStk(stk)
struct  stack   *stk;
{
    int i;
    for (i = 0; i < maxN; i++)
        stk->node[i] = ' ';
    stk->sp = -1;   
}

void    pushIn(stk, node)
struct  stack   *stk;
char    node;
{

    stk->sp++;
    stk->node[stk->sp] = node;

}    

void    popOutAll(stk)
struct  stack   *stk;
{

    char    node;
    int i, stkN = stk->sp;

    for (i = 0; i <= stkN; i++)
    {
        node = stk->node[i];
        if (i == 0)
            printf("src node : %c", node);
        else if (i == stkN)
            printf(" => %c : dst node.\n", node);
        else
            printf(" => %c ", node);
    }

}


/* Test whether the node already exists in the stack    */
bool    InStack(stk, InterN)
struct  stack   *stk;
char    InterN;
{

    int i, stkN = stk->sp;  /* 0-based  */
    bool    rtn = false;    

    for (i = 0; i <= stkN; i++)
    {
        if (stk->node[i] == InterN)
        {
            rtn = true;
            break;
        }
    }

    return     rtn;

}

char    otherNode(targetNode, lnkNode)
char    targetNode;
struct  nodeLink    *lnkNode;
{

    return  (lnkNode->node1 == targetNode) ? lnkNode->node2 : lnkNode->node1;

}

int entries = 8;
struct  nodeLink    topo[maxN]    =       
    {
        {'b', 'a'}, 
        {'b', 'e'}, 
        {'b', 'd'}, 
        {'f', 'b'}, 
        {'a', 'c'},
        {'c', 'f'}, 
        {'c', 'e'},
        {'f', 'e'},               
    };

char    srcNode = 'b', dstN = 'e';      

int reachTime;  

void    InterNode(interN, stk)
char    interN;
struct  stack   *stk;
{

    char    otherInterN;
    int i, numInterN = 0;
    static  int entryTime   =   0;

    entryTime++;

    for (i = 0; i < entries; i++)
    {

        if (topo[i].node1 != interN  && topo[i].node2 != interN) 
        {
            continue;   
        }

        otherInterN = otherNode(interN, &topo[i]);

        numInterN++;

        if (otherInterN == stk->node[stk->sp - 1])
        {
            continue;   
        }

        /*  Loop avoidance: abandon the route   */
        if (InStack(stk, otherInterN) == true)
        {
            continue;   
        }

        pushIn(stk, otherInterN);

        if (otherInterN == dstN)
        {
            popOutAll(stk);
            reachTime++;
            stk->sp --;   /*    back trace one node  */
            continue;
        }
        else
            InterNode(otherInterN, stk);

    }

        stk->sp --;

}


int    main()

{

    struct  stack   stk;

    initStk(&stk);
    pushIn(&stk, srcNode);  

    reachTime = 0;
    InterNode(srcNode, &stk);

    printf("\nNumber of all possible and unique routes = %d\n", reachTime);

}
于 2012-02-12T08:24:28.677 に答える
3

これは遅いかもしれませんが、これは Casey による Java の DFS アルゴリズムの同じ C# バージョンで、スタックを使用して 2 つのノード間のすべてのパスをトラバースします。いつものように再帰的に読みやすくなっています。

    void DepthFirstIterative(T start, T endNode)
    {
        var visited = new LinkedList<T>();
        var stack = new Stack<T>();

        stack.Push(start);

        while (stack.Count != 0)
        {
            var current = stack.Pop();

            if (visited.Contains(current))
                continue;

            visited.AddLast(current);

            var neighbours = AdjacentNodes(current);

            foreach (var neighbour in neighbours)
            {
                if (visited.Contains(neighbour))
                    continue;

                if (neighbour.Equals(endNode))
                {
                    visited.AddLast(neighbour);
                    printPath(visited));
                    visited.RemoveLast();
                    break;
                }
            }

            bool isPushed = false;
            foreach (var neighbour in neighbours.Reverse())
            {
                if (neighbour.Equals(endNode) || visited.Contains(neighbour) || stack.Contains(neighbour))
                {
                    continue;
                }

                isPushed = true;
                stack.Push(neighbour);
            }

            if (!isPushed)
                visited.RemoveLast();
        }
    }
これは、テストするサンプル グラフです。

    // サンプル グラフ。数字はエッジ ID
    // 1 3       
    // A --- B --- C ----
    // | | | 2 |
    // | 4 ----- D |
    // ------------------
于 2014-10-17T16:44:02.613 に答える
1

この背後にある実際の問題を説明する必要があると思います。私がこれを言うのは、あなたが時間効率の良いものを求めているからですが、問題に対する答えは指数関数的に増加しているようです!

したがって、指数関数よりも優れたアルゴリズムは期待できません。

バックトラックしてグラフ全体を調べます。サイクルを回避するために、途中で訪問したすべてのノードを保存します。戻ったら、ノードのマークを外します。

再帰の使用:

static bool[] visited;//all false
Stack<int> currentway; initialize empty

function findnodes(int nextnode)
{
if (nextnode==destnode)
{
  print currentway 
  return;
}
visited[nextnode]=true;
Push nextnode to the end of currentway.
for each node n accesible from nextnode:
  findnodes(n);
visited[nextnode]=false; 
pop from currenteay
}

それとも間違っていますか?

編集:ああ、忘れていました:そのノードスタックを利用して再帰呼び出しを排除する必要があります

于 2008-09-12T07:24:47.927 に答える
1

基本的な原則は、グラフについて心配する必要はないということです。これは、動的接続問題として知られる標準的な問題です。ノードが接続されているかどうかを確認できる方法には、次の種類があります。

  1. クイック検索
  2. クイックユニオン
  3. 改善されたアルゴリズム (両方の組み合わせ)

これは、最小限の時間の複雑さ O(log*n) で試した C コードです。つまり、エッジのリストが 65536 の場合は 4 回の検索が必要で、2^65536 の場合は 5 回の検索が必要です。アルゴリズムからの実装を共有しています:プリンストン大学のアルゴリズムコース

ヒント: Java ソリューションは、上記で共有された適切な説明付きのリンクから見つけることができます。

/* Checking Connection Between Two Edges */

#include<stdio.h>
#include<stdlib.h>
#define MAX 100

/*
  Data structure used

vertex[] - used to Store The vertices
size - No. of vertices
sz[] - size of child's
*/

/*Function Declaration */
void initalize(int *vertex, int *sz, int size);
int root(int *vertex, int i);
void add(int *vertex, int *sz, int p, int q);
int connected(int *vertex, int p, int q);

int main() //Main Function
{ 
char filename[50], ch, ch1[MAX];
int temp = 0, *vertex, first = 0, node1, node2, size = 0, *sz;
FILE *fp;


printf("Enter the filename - "); //Accept File Name
scanf("%s", filename);
fp = fopen(filename, "r");
if (fp == NULL)
{
    printf("File does not exist");
    exit(1);
}
while (1)
{
    if (first == 0) //getting no. of vertices
    {
        ch = getc(fp);
        if (temp == 0)
        {
            fseek(fp, -1, 1);
            fscanf(fp, "%s", &ch1);
            fseek(fp, 1, 1);
            temp = 1;
        }
        if (isdigit(ch))
        {
            size = atoi(ch1);
            vertex = (int*) malloc(size * sizeof(int));     //dynamically allocate size  
            sz = (int*) malloc(size * sizeof(int));
            initalize(vertex, sz, size);        //initialization of vertex[] and sz[]
        }
        if (ch == '\n')
        {
            first = 1;
            temp = 0;
        }
    }
    else
    {
        ch = fgetc(fp);
        if (isdigit(ch))
            temp = temp * 10 + (ch - 48);   //calculating value from ch
        else
        {
            /* Validating the file  */

            if (ch != ',' && ch != '\n' && ch != EOF)
            {
                printf("\n\nUnkwown Character Detected.. Exiting..!");

                exit(1);
            }
            if (ch == ',')
                node1 = temp;
            else
            {
                node2 = temp;
                printf("\n\n%d\t%d", node1, node2);
                if (node1 > node2)
                {
                    temp = node1;
                    node1 = node2;
                    node2 = temp;
                }

                /* Adding the input nodes */

                if (!connected(vertex, node1, node2))
                    add(vertex, sz, node1, node2);
            }
            temp = 0;
        }

        if (ch == EOF)
        {
            fclose(fp);
            break;
        }
    }
}

do
{
    printf("\n\n==== check if connected ===");
    printf("\nEnter First Vertex:");
    scanf("%d", &node1);
    printf("\nEnter Second Vertex:");
    scanf("%d", &node2);

    /* Validating The Input */

    if( node1 > size || node2 > size )
    {
        printf("\n\n Invalid Node Value..");
        break;
    }

    /* Checking the connectivity of nodes */

    if (connected(vertex, node1, node2))
        printf("Vertex %d and %d are Connected..!", node1, node2);
    else
        printf("Vertex %d and %d are Not Connected..!", node1, node2);


    printf("\n 0/1:  ");

    scanf("%d", &temp);

} while (temp != 0);

free((void*) vertex);
free((void*) sz);


return 0;
}

void initalize(int *vertex, int *sz, int size) //Initialization of graph
{
int i;
for (i = 0; i < size; i++)
{
    vertex[i] = i;
    sz[i] = 0;
}
}
int root(int *vertex, int i)    //obtaining the root
{
while (i != vertex[i])
{
    vertex[i] = vertex[vertex[i]];
    i = vertex[i];
}
return i;
}

/* Time Complexity for Add --> logn */
void add(int *vertex, int *sz, int p, int q) //Adding of node
{
int i, j;
i = root(vertex, p);
j = root(vertex, q);

/* Adding small subtree in large subtree  */

if (sz[i] < sz[j])
{
    vertex[i] = j;
    sz[j] += sz[i];
}
else
{
    vertex[j] = i;
    sz[i] += sz[j];
}

}

/* Time Complexity for Search -->lg* n */

int connected(int *vertex, int p, int q) //Checking of  connectivity of nodes
{
/* Checking if root is same  */

if (root(vertex, p) == root(vertex, q))
    return 1;

return 0;
}
于 2014-07-29T10:48:24.187 に答える
1

最近、これと同様の問題を解決しましたが、すべてのソリューションではなく、最短のものにしか興味がありませんでした。

ステータスのキューを使用する「幅優先」の反復検索を使用しました。各キューには、グラフ上の現在のポイントとそこに到達するまでのパスを含むレコードが保持されていました。

開始ノードと空のパスを持つキュー内の単一のレコードから開始します。

コードを反復するたびに、リストの先頭から項目が取り出され、それが解決策であるかどうかがチェックされます (到達したノードは目的のノードであり、そうであれば完了です)。そうでない場合は、新しいノードを構築します現在のノードに接続しているノードと、前のノードのパスに基づく修正されたパスを含むキュー アイテムで、最後に新しいジャンプが追加されています。

さて、似たようなものを使用できますが、解決策を見つけたら、停止する代わりに、その解決策を「見つかったリスト」に追加して続行します。

訪問したノードのリストを追跡する必要があります。そうしないと、無限ループが発生します。

もう少し疑似コードが必要な場合は、コメントか何かを投稿してください。詳しく説明します。

于 2008-09-12T04:49:42.543 に答える
0

Ryan Fox ( 58343、Christian ( 58444 ) 、およびあなた自身 ( 58461 ) )によって与えられた解決策を私が知る限り、それが得られるのと同じくらい良いです。幅優先走査がこの場合に役立つとは思いません。すべてのパスを取得するわけではありません。たとえば、エッジ(A,B)(A,C)(B,C)(B,D)および(C,D)を使用すると、パスABDおよびは取得されますが、 は取得さACDれませんABCD

于 2008-09-12T08:29:27.647 に答える
0

他のポスターの何人かによって適切に説明されているように、問題は一言で言えば、深さ優先検索アルゴリズムを使用して、通信するエンドノード間のパスのすべての組み合わせについてグラフを再帰的に検索することです。

アルゴリズム自体は、指定された開始ノードから始まり、すべての発信リンクを調べ、表示される検索ツリーの最初の子ノードを展開して進行し、ターゲット ノードが見つかるまで、またはノードに遭遇するまで、徐々に深く検索します。それには子供がいません。

その後、検索はバックトラックし、まだ探索を終了していない最新のノードに戻ります。

私はごく最近、このトピックについてブログを書き、その過程で C++ 実装の例を投稿しました。

于 2011-09-22T21:38:20.423 に答える
0

ループを含む無限パスを含むすべてのパスを列挙する方法を見つけました。

http://blog.vjeux.com/2009/project/project-shortest-path.html

原子パスとサイクルを見つける

Definition

私たちがやりたいことは、点 A から点 B に向かう可能性のあるすべてのパスを見つけることです。サイクルが含まれているため、すべてを調べて列挙することはできません。代わりに、ループしないアトミック パスと可能な限り最小のサイクルを見つける必要があります (サイクルが繰り返されないようにする必要があります)。

アトミック パスについて最初に定義したのは、同じノードを 2 回通過しないパスです。しかし、それはすべての可能性を取っているわけではないことがわかりました。熟考した結果、ノードは重要ではなく、エッジは重要であることがわかりました。したがって、アトミック パスとは、同じエッジを 2 回通過しないパスです。

この定義は便利で、サイクルにも機能します。ポイント A のアトミック サイクルは、ポイント A からポイント A に至るアトミック パスです。

実装

Atomic Paths A -> B

ポイント A から始まるすべてのパスを取得するために、ポイント A から再帰的にグラフをトラバースします。すでに越えています。その子に移動する前に、リンクされたリストをトラバースし、指定されたエッジがまだ通過していないことを確認する必要があります。

目的地に到着したら、見つけた経路を保存できます。

Freeing the list

リンクされたリストを解放したいときに問題が発生します。それは基本的に逆の順序で連鎖されたツリーです。解決策は、そのリストをダブルリンクし、すべてのアトミックパスが見つかったら、ツリーを開始点から解放することです。

しかし、賢明な解決策は、参照カウントを使用することです (ガベージ コレクションから着想を得ています)。親にリンクを追加するたびに、その参照カウントに 1 つ追加されます。次に、パスの最後に到達すると、参照カウントが 1 に等しい間、逆方向に移動して解放します。それよりも大きい場合は、1 つ削除して停止します。

Atomic Cycle A

A のアトミック サイクルを探すことは、A から A へのアトミック パスを探すことと同じです。ただし、実行できる最適化がいくつかあります。まず、目的地に到着したとき、エッジ コストの合計が負の場合にのみパスを保存します。つまり、吸収サイクルを通過したいだけです。

前に見たように、アトミック パスを探すときにグラフ全体がトラバースされます。代わりに、検索領域を A を含む強連結成分に限定することができます。これらの成分を見つけるには、Tarjan のアルゴリズムを使用してグラフを単純にトラバースする必要があります。

アトミック パスとサイクルの組み合わせ

この時点で、A から B に至るすべてのアトミック パスと、各ノードのすべてのアトミック サイクルが得られたので、最短パスを取得するためにすべてを整理する必要があります。これからは、原子パス内の原子サイクルの最適な組み合わせを見つける方法を研究します。

于 2010-12-07T10:56:23.023 に答える
0

ここに私の頭の上の考えがあります:

  1. 接続を 1 つ見つけます。(パスの長さは問題にならないため、深さ優先検索はおそらくこれに適したアルゴリズムです。)
  2. 最後のセグメントを無効にします。
  3. 以前に無効にした接続の前の最後のノードから別の接続を見つけようとします。
  4. 接続がなくなるまで 2 に移動します。
于 2008-09-12T05:11:02.630 に答える
0

Casey Watson の回答に加えて、別の Java 実装を次に示します。訪問したノードを開始ノードで初期化します。

private void getPaths(Graph graph, LinkedList<String> visitedNodes) {
                LinkedList<String> adjacent = graph.getAdjacent(visitedNodes.getLast());
                for(String node : adjacent){
                    if(visitedNodes.contains(node)){
                        continue;
                    }
                    if(node.equals(END)){
                        visitedNodes.add(node);
                        printPath(visitedNodes);
                        visitedNodes.removeLast();
                    }
                    visitedNodes.add(node);
                    getPaths(graph, visitedNodes);
                    visitedNodes.removeLast();  
                }
            }
于 2016-08-26T18:39:39.993 に答える