Algorithm to find lowest common ancestor in directed acyclic graph?

后端 未结 10 837
孤独总比滥情好
孤独总比滥情好 2020-12-02 11:34

Imagine a directed acyclic graph as follows, where:

  • \"A\" is the root (there is always exactly one root)
  • each node knows its parent(s)
  • the no
相关标签:
10条回答
  • 2020-12-02 11:51

    Everyone. Try please in Java.

    static String recentCommonAncestor(String[] commitHashes, String[][] ancestors, String strID, String strID1)
    {
        HashSet<String> setOfAncestorsLower = new HashSet<String>();
        HashSet<String> setOfAncestorsUpper = new HashSet<String>();
        String[] arrPair= {strID, strID1};
        Arrays.sort(arrPair);
        Comparator<String> comp = new Comparator<String>(){
            @Override
            public int compare(String s1, String s2) {
               return s2.compareTo(s1);
            }};
        int indexUpper = Arrays.binarySearch(commitHashes, arrPair[0], comp);
        int indexLower = Arrays.binarySearch(commitHashes, arrPair[1], comp);
        setOfAncestorsLower.addAll(Arrays.asList(ancestors[indexLower]));
        setOfAncestorsUpper.addAll(Arrays.asList(ancestors[indexUpper]));
        HashSet<String>[] sets = new HashSet[] {setOfAncestorsLower, setOfAncestorsUpper};
        for (int i = indexLower + 1; i < commitHashes.length; i++)
        {
            for (int j = 0; j < 2; j++)
            {
                if (sets[j].contains(commitHashes[i]))
                {
                    if (i > indexUpper)
                        if(sets[1 - j].contains(commitHashes[i]))
                            return commitHashes[i];
                    sets[j].addAll(Arrays.asList(ancestors[i]));
                }
            }
        }
        return null;
    }
    

    The idea is very simple. We suppose that commitHashes ordered in downgrade sequence. We find lowest and upper indexes of strings(hashes-does not mean). It is clearly that (considering descendant order) the common ancestor can be only after upper index (lower value among hashes). Then we start enumerating the hashes of commit and build chain of descendent parent chains . For this purpose we have two hashsets are initialised by parents of lowest and upper hash of commit. setOfAncestorsLower, setOfAncestorsUpper. If next hash -commit belongs to any of chains(hashsets), then if current index is upper than index of lowest hash, then if it is contained in another set (chain) we return the current hash as result. If not, we add its parents (ancestors[i]) to hashset, which traces set of ancestors of set,, where the current element contained. That is the all, basically

    0 讨论(0)
  • 2020-12-02 11:56

    I was looking for a solution to the same problem and I found a solution in the following paper:

    http://dx.doi.org/10.1016/j.ipl.2010.02.014

    In short, you are not looking for the lowest common ancestor, but for the lowest SINGLE common ancestor, which they define in this paper.

    0 讨论(0)
  • 2020-12-02 11:56

    I know it's and old question and pretty good discussion, but since I had some similar problem to solve I came across JGraphT's Lowest Common Ancestor algorithms, thought this might be of help:

    • NativeLcaFinder
    • TarjanLowestCommonAncestor
    0 讨论(0)
  • 2020-12-02 11:56

    This link (Archived version) describes how it is done in Mercurial - the basic idea is to find all parents for the specified nodes, group them per distance from the root, then do a search on those groups.

    0 讨论(0)
  • 2020-12-02 12:04

    Den Roman's link (Archived version) seems promising, but it seemed a little bit complicated to me, so I tried another approach. Here is a simple algorithm I used:

    Let say you want to compute LCA(x,y) with x and y two nodes. Each node must have a value color and count, resp. initialized to white and 0.

    1. Color all ancestors of x as blue (can be done using BFS)
    2. Color all blue ancestors of y as red (BFS again)
    3. For each red node in the graph, increment its parents' count by one

    Each red node having a count value set to 0 is a solution.

    There can be more than one solution, depending on your graph. For instance, consider this graph:

    LCA(4,5) possible solutions are 1 and 2.

    Note it still work if you want find the LCA of 3 nodes or more, you just need to add a different color for each of them.

    0 讨论(0)
  • 2020-12-02 12:05

    If the graph has cycles then 'ancestor' is loosely defined. Perhaps you mean the ancestor on the tree output of a DFS or BFS? Or perhaps by 'ancestor' you mean the node in the digraph that minimizes the number of hops from E and B?

    If you're not worried about complexity, then you could compute an A* (or Dijkstra's shortest path) from every node to both E and B. For the nodes that can reach both E and B, you can find the node that minimizes PathLengthToE + PathLengthToB.

    EDIT: Now that you've clarified a few things, I think I understand what you're looking for.

    If you can only go "up" the tree, then I suggest you perform a BFS from E and also a BFS from B. Every node in your graph will have two variables associated with it: hops from B and hops from E. Let both B and E have copies of the list of graph nodes. B's list is sorted by hops from B while E's list is sorted by hops from E.

    For each element in B's list, attempt to find it in E's list. Place matches in a third list, sorted by hops from B + hops from E. After you've exhausted B's list, your third sorted list should contain the LCA at its head. This allows for one solution, multiple solutions(arbitrarily chosen among by their BFS ordering for B), or no solution.

    0 讨论(0)
提交回复
热议问题