【问题标题】:Converging maze: Largest cycle会聚迷宫:最大循环
【发布时间】:2016-07-26 07:13:09
【问题描述】:

这个问题是在一次采访中提出的,我仍在寻找最佳解决方案。

给你一个有 N 个单元的迷宫。每个单元格可以有多个入口点,但不能超过一个出口 (即入口/出口点是单向门,如阀门)。

单元格以从 0 开始的整数值命名 到 N-1。

你需要找到迷宫中最大循环的长度。如果没有循环,则返回 -1。

输入格式

  • 第一行的单元格数为 N
  • 第二行列出了 edge[] 数组的 N 个值。 edge[i] 包含的单元格编号 可以从单元格“i”一步到达。如果第 i 个单元格没有出口,则 edge[i] 为 -1。

输出格式

  • 最大循环的长度。

示例输入:

23

4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21

样本输出

6

我已经尝试使用 DFS 来查找所有可能的循环并打印最大的循环大小。 请让我知道是否有更好的解决方案。

【问题讨论】:

    标签: algorithm data-structures


    【解决方案1】:

    给定图中的一个节点,从它开始有一条唯一的最大路径(因为从任何节点到最多有一个出口)。它可能会或可能不会循环。

    很容易找到从一个节点开始的最终循环长度:不断跟随出口节点,沿路径记录一组节点。当您找不到出口节点,或者您将要访问以前访问过的节点时停止。如果没有出口节点就没有循环,否则你可以从之前访问过的节点开始找到循环长度,然后重新跟踪循环。 [你也可以在这里使用 Floyd 算法,这需要 O(1) 而不是 O(N) 的存储空间,但我们将在下一步中使用 O(N) 的存储空间]。

    使用这个,可以在 O(N) 时间内找到最大循环:对图中的每个节点重复上述算法,但缓存结果(如果没有找到循环,则存储 -1)。如果您在路径上找到以前缓存的结果,则必须小心停止上面的循环查找,并且一旦找到某个节点的结果,您必须缓存该路径上所有节点的结果,直到找到一个结果已经被缓存的节点。最大循环的大小就是最大缓存值的值。

    这是 O(N) 运行时:图中的每条边(最多有 N 条)最多被跟踪 3 次,并且图中每个节点的缓存只更新一次。它使用 O(N) 额外的存储空间。

    【讨论】:

    • 所以这将是一个 O(N*N) 复杂的权利。有没有其他可以优化的。
    • @Bhavesh 不,这不是 O(N^2),而是 O(N),正如我在回答中所展示的那样。是否有一些不清楚的部分让你认为它是二次的?
    • 好吧,你的意思是如果在成功检测循环的同时节点已经被访问过,我们不应该再次访问那个节点。
    【解决方案2】:
    public static int solution(int arr[])
    {
        ArrayList<Integer> sum = new ArrayList<>();
        for(int i = 0; i < arr.length; i ++)
        {
            ArrayList<Integer> path = new ArrayList<>();    
            int j = i;
            int tempSum = 0;
            while(arr[j]<arr.length && arr[j]!=i && arr[j]!=-1 && !path.contains(j))
            {   
                path.add(j);
                tempSum+=j;
                j=arr[j];
                if(arr[j]==i)
                {
                    tempSum+=j;
                    break;
                }
            }
            if(j<arr.length && i == arr[j])
                sum.add(tempSum);
        }
            if(sum.isEmpty())
                return -1;
            return Collections.max(sum);
    }
    

    【讨论】:

    • 一些介绍性文字在答案中总是有用的。
    【解决方案3】:

    这是一个 JavaScript 实现。我没有使用 JavaScript 的任何花哨功能,因此从代码中可以很容易地看到该算法。另一方面,它确实需要 ES6 支持才能运行(忘记 IE):

    function largestCycle(edges) {
        var result, visitedFrom, startCell, cell, cells;
        
        result = [];
        visitedFrom = Array(edges.length).fill(-1);
        for (startCell = 0; startCell < edges.length; startCell++) {
            cells = [];
            for (cell=startCell; cell>-1 && visitedFrom[cell]===-1; cell = edges[cell]) {
                visitedFrom[cell] = startCell;
                cells.push(cell);
            }
            if (cell > -1 && visitedFrom[cell] === startCell) {
                cells = cells.slice(cells.indexOf(cell));
                if (cells.length > result.length) result = cells;
            }
        }
        return result;
    }
    
    // Snippet I/O
    
    var input = document.querySelector('textarea');
    var output = document.querySelector('span');
    
    (input.oninput = function () {
        // Get input as array of numbers
        var edges = input.value.trim().split(/\s+/).map(Number);
        // Apply algorithm
        var cycle = largestCycle(edges);
        // Output result
        output.textContent = cycle.length + ': ' + JSON.stringify(cycle);
    })(); // Execute also at page load
    Input:<br>
    <textarea style="width:100%">4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21</textarea><br>
    Greatest Cycle: <span></span>

    这在 O(n) 中运行。即使外循环有嵌套循环和迭代数组的表达式(使用sliceindexOf),这些子迭代每个单元格只执行一次,所以总的来说这仍然是O (n).

    该函数不仅返回循环大小,还返回包含属于该循环的单元格列表的数组。这是一个很小的开销,但可以更好地验证结果。

    【讨论】:

      【解决方案4】:

      trincot 建议的解决方案的 Python 实现。 说明:

      1. 遍历每个节点
      2. 对于每个节点,使用索引导航到下一个节点。例如(第一次迭代:外部 for 循环) 从第 0 个索引我们可以达到 4 ,从第 4 个索引我们可以达到 13 ,从第 13 个索引我们可以达到 11 ,依此类推,直到我们在我们的案例 0 中再次到达访问节点, viola,我们找到了第一个循环。
      3. 检查 visitedFrom[cell] == startCell 即 0 是否将其添加到 result 数组中。
      4. 重复下一个节点(步骤 1)

      代码


      def largestCycle(edges):
          result = []
          visitedFrom = [-1] * len(edges)
          for startCell in range(0, len(edges)):
              cells = []
              cell = startCell
              while cell > -1 and visitedFrom[cell] == -1:
                  visitedFrom[cell] = startCell
                  cells.append(cell)
                  cell = edges[cell]
              if cell > -1 and visitedFrom[cell] == startCell:
                  cells_idx = cells.index(cell)
                  cells = cells[cells_idx:]
                  if len(cells) > len(result):
                      result = cells
      
          return result,len(result)
      
      size = 23
      edges = [4, 4, 1, 4, 13, 8, 8, 8, 0, 8, 14, 9, 15, 11, -1, 10, 15, 22, 22, 22, 22, 22, 21]
      largestCycle(edges)
      

      【讨论】:

        【解决方案5】:

        使用 Prims 算法查找节点中的最大循环

        n = int(input())
        v = n
        e = v+1
        arr = [int(i) for i in input().split()]
        graph = [[0 for _ in range(n)] for _ in range(n)]
        for i in range(0, len(arr)):
            graph[i][arr[i]] = 1
        for i in graph:
            print(i)
            
            
            def min_ind(wieight, visied):
                min_ = -1
                ind = -1
                for i in range(v):
                    if(wieight[i] > min_ and visied[i] == False):
                        min_ = wieight[i]
                        ind = i
                return ind
            
            
            def printPath(parent, i):
                res = []
                while(parent[i] != -1):
                    res.append(i)
                    i = parent[i]
                res.append(i)
                return res[::-1]
            
            
            # Dijkstra
            visited = [False for _ in range(v)]
            wieight = [0 for _ in range(v)]
            parent = [-1 for i in range(v)]
            wieight[0] = 0
            path = []
            for _ in range(v):
                u = min_ind(wieight, visited)
                if(u == -1):
                    continue
                visited[u] = True
                for i in range(v):
                    if(graph[u][i] > 0 and visited[i] == False):
                        if(wieight[i] < graph[u][i]):
                            wieight[i] = graph[u][i]
                            parent[i] = u
            maximum = 0
            for i in range(0, len(wieight)):
                print("No:", i, " Weight:", wieight[i], " Path:", end=" ")
                path = (printPath(parent, i))
                maximum = max(maximum, len(path))
                print(path, end=" ")
                print()
            print("Longest Cycle: ", maximum)
        

        【讨论】:

          【解决方案6】:

          这是问题的解决方案,但输入格式实际上并不相同。

          这是输入格式

          test cases: N  
          size of array: M  
          array elements: 1<=a(i)<=M-1 where 0<=i<=M-1  
          index to which last index points: C
          

          在这个问题中,我们要计算最大循环中的单元格,代码如下:

          class countLargestCycleMaze {
          
            static vertex[] cells;
          
            static class vertex {
              int name;
              neighbor list;
              public vertex(int v, neighbor nb) {
                this.name = v;
                this.list = nb;
              }
            }
          
            static class neighbor {
              int vnum;
              neighbor next;
              public neighbor(int v, neighbor nb) {
                this.vnum = v;
                this.next = nb;
              }
            }
          
            static int dfs(int v, int m) {
              neighbor tmp = cells[v].list;
              int c = 0;
          
              while (tmp.vnum != m)
                tmp = cells[tmp.vnum].list;
          
              tmp = cells[tmp.vnum].list;
          
              while (tmp.vnum != m) {
                tmp = cells[tmp.vnum].list;
                c++;
              }
          
              return c;
            }
          
            public static void main(String[] args) throws java.lang.Exception {
              try {
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
          
                int i, j, n, m, c;
          
                n = Integer.parseInt(br.readLine());
                while (n-- > 0) {
                  m = Integer.parseInt(br.readLine());
                  StringTokenizer st = new StringTokenizer(br.readLine());
                  c = Integer.parseInt(br.readLine());
                  if (c == 0) {
                    System.out.println("0");
                    continue;
                  }
                  cells = new vertex[m + 1];
                  for (i = 0; i < m; i++) {
                    int num = Integer.parseInt(st.nextToken());
                    cells[i] = new vertex(num, null);
                    cells[i].list = new neighbor(num, cells[i].list);
                  }
                  cells[m] = new vertex(c, null);
                  cells[m].list = new neighbor(c, cells[m].list);
          
                  System.out.println(dfs(0, c));
                }
              } catch (Exception e) {}
            }
          }
          

          【讨论】:

            【解决方案7】:
            public class LargestCycleInGraph {
                public static int checkCycle(int []cell , int size , int start) {
                    Set<Integer> set = new HashSet<>();
                    set.add(start);
                    for(int i = start ;i< size;i++) {
                        if( !set.contains(cell[i]) && cell[i] != -1) {
                            set.add( cell[i] );
                        }
                        else return set.size() + 1; // 1 for again come to cycle node
                    }
                    return -1;
                }
                public static int findLargestCycle(int []cell , int size) {
                    int max = -1;
                    for(int i =0;i<size;i++) {
                        //if you want to find sum of largest cycle return "Set" rather than its size and check for max sum
                        int cyc = checkCycle(cell , size , i); 
                        if(max < cyc)
                            max = cyc;
                      }
                    return max;
                }
                public static void main(String[] args) {
                    int size = 23;
                    int []cell = {4, 4, 1, 4, 13, 8, 8, 8, 0, 8, 14, 9, 15, 11, -1, 10, 15, 22, 22, 22, 22, 22, 21};
                    int largestCycle = findLargestCycle(cell , size);
                    System.out.println("Largest cycle Length " +largestCycle);  
                }
            }
            

            【讨论】:

            • 嗨,欢迎来到 Stack Overflow。在回答已经有很多答案的问题时,请务必添加一些额外的见解,说明为什么您提供的回复是实质性的,而不是简单地呼应原始发帖人已经审查过的内容。这在您提供的“纯代码”答案中尤其重要。
            • 我应该如何添加最大循环总和
            【解决方案8】:
            def main():
                size = int(input())
                cell = input().split()
            
                for i in range(0, len(cell)):
                    cell[i] = int(cell[i])
                m = -1
                for i in range(0, 23):
                    if m < check_cycle(cell, i):
                        m = check_cycle(cell, i)
                print("Largest cycle is made of", m, "nodes")
            
            
            def check_cycle(cell, start):
                i = start
                if i in cell:
                    cycle = [i]
                    j = i
                    while 1:
                        for k in cycle:
                            if cycle.count(k) >= 2:
                                if cycle[0] == cycle[-1]:
                                    return len(cycle)-1
                                else:
                                    return 0
                        else:
                            cycle.append(cell[j])
                            j = cell[j]
                else:
                    return 0
            
            
            main()
            

            【讨论】:

            • 感谢您加入 Stackoverflow 并回答问题。除了发布一堆代码之外,您能否简要说明您解决的问题是什么,以及您的代码如何解决它?这将帮助人们更好地了解您的解决方案,并将其用于自己的改进。
            【解决方案9】:

            这是我的尝试,遍历图中的每个节点:-

            #include <stdio.h>
            
            int findingLargestCycle(int noOfInputs, int *edges){
                int largestCycle = 0;
                for(int i=0; i<noOfInputs; i++){
                    int currentEdge = edges[i];
                    int count = currentEdge;
                    int nextEdge = edges[currentEdge];
            
                    int n = 0;
                    while(currentEdge != nextEdge && n<noOfInputs+1){
                        if(nextEdge == -1 || currentEdge == -1){
                            count = 0;
                            break;
                        }
            
                        if(nextEdge != currentEdge){
                            count += nextEdge;
                        }
            
                        nextEdge = edges[nextEdge];
                        n++;
                    }
            
                    if(count > largestCycle && n != noOfInputs+1){
                        largestCycle = count;
                    }
                }
                return largestCycle;
            }
            
            int main(){
                int testCases;
                scanf("%d", &testCases);
                int numberOfInputs;
                scanf("%d", &numberOfInputs);
                int edges[numberOfInputs];
                for(int i=0; i<numberOfInputs; i++){
                    scanf("%d", &edges[i]);
                }
            
                printf("%d", findingLargestCycle(numberOfInputs, &edges[0]));
            }
            
            

            【讨论】:

              【解决方案10】:

              O(n)时间复杂度解每个节点只有在检查它之前是否访问过之后才被访问,所以每个节点只被访问一次。

              O(n) 空间复杂度([n]:stack space max + [2*n]:two map used max size)

              观察:两个节点之间总是有唯一的路径(检查任何测试用例),由于条件,每个节点只有一个出口。

              C++ 代码:

              #include <iostream>
              #include <vector>
              #include <unordered_map>
              using namespace std;
              //res stores result
              int res = 0;
              //visit to check in before visiting the node, to stop repeat visiting
              unordered_map<int,bool> visit;
              void dfs(vector<int> & a, unordered_map<int,int> &mp, int i, int k){
                  if(visit.find(i) != visit.end())
                      return;
                  if(a[i] == -1){
                      visit[i] = true;
                      return;
                  }
                  if(mp.find(i) != mp.end()){
                      res = max(res, k-mp[i]);
                      visit[i] = true;
                      return;
                  }
                  mp[i] = k;
                  dfs(a, mp, a[i], k+1);
                  visit[i] = true;
              }
              
              int main() {
                  int n;
                  cin>>n;
                  vector<int> a(n,0);
                  for(int i=0;i<n;i++)
                      cin>>a[i];
                  for(int i=0;i<n;i++){
                      if(visit.find(i) == visit.end()){
                          unordered_map<int,int> mp;
                          dfs(a, mp, i, 0);
                      }
                  }
                  cout<<res<<endl;
                  return 0;
              }
              

              【讨论】:

                【解决方案11】:

                C++ 中的解决方案

                #include <bits/stdc++.h>
                using namespace std;
                bool isCycle(vector<int> arr, int curr, vector<bool> &visited, vector<int> &path)
                {
                    if (curr == -1)
                    {
                        return false;
                    }
                    if (visited[curr])
                    {
                        return true;
                    }
                    visited[curr] = true;
                    path.emplace_back(curr);
                    if (isCycle(arr, arr[curr], visited, path))
                        return true;
                    return false;
                }
                
                int largestSumCycle(vector<int> arr)
                {
                    int n = arr.size();
                    int sum = INT_MIN;
                    vector<bool> visited(n, false);
                    for (int i = 0; i < n; i++)
                    {
                        visited[i] = true;
                
                        vector<int> path;
                
                        if (isCycle(arr, arr[i], visited, path))
                            sum = max(sum, accumulate(path.begin(), path.end(), 0));
                
                        visited[i] = false;
                    }
                    if (sum == INT_MIN)
                        return -1;
                    return sum;
                }
                

                【讨论】:

                  【解决方案12】:

                  这是面试中常见的问题,在同一次面试中,他们也问了这个问题,询问的细节相同。

                  问:找到最近的会议室 (NMC)

                  INPUT : 同上 + 第三行有 2 个号码,要找到最近的会议单元。

                  示例输入

                  23

                  4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21

                  9 2 (需要在网格/图中找出 9 , 2 的交汇点)

                  输出

                  4

                  代码:

                  def main():
                  testCASES=int(input())
                  # testCASES=1
                  for case_number in range(testCASES):
                      meshsize=input()
                  
                      mesh=input()
                      # mesh='4 4 1 4 13 8 8 8 0 8 14 9 15 11 -1 10 15 22 22 22 22 22 21'
                      det=input()
                      # det='9 2'
                  
                      mesh=[int(x) for x in mesh.split()]
                      det=[int(x) for x in det.split()]
                  
                      n1=det[0]
                      n2=det[1]
                  
                      n1path=[]
                      n2path=[]
                  
                      for i in range(len(mesh)):
                          if not n1path:
                              n1path.append(mesh[n1])
                          else:
                              n1path.append(mesh[n1path[i-1]])
                  
                          if not n2path:
                              n2path.append(mesh[n2])
                          else:
                              n2path.append(mesh[n2path[i-1]])
                  
                      nearestList=[]
                      try:
                          for x in n1path:
                              nearestList.append(n2path.index(x))
                          NEAREST_NODE=n2path[min(nearestList)]
                      except Exception as e:
                          NEAREST_NODE = -1
                  
                      # print(n1path,n2path)
                      print(NEAREST_NODE)
                  

                  main()

                  工作:

                  从给定的 2 个点开始走路径,并通过对最近列表的索引使用 min() 函数计算 n1path 和 n2path 的第一个公共点。命名是任意的,但这是核心算法。

                  如果存在循环,它可以处理,并且仍然返回第一个交点。 如果没有找到匹配项,则返回 -1。

                  【讨论】:

                    【解决方案13】:

                    这是问题的另一种变体,除了正常的 inpt 之外,我们有两个节点,src 和 dest,我们必须输出最接近 src 和 dest 的节点。

                    这是我从 src 和 dest 查找最近单元格的解决方案

                    #include<bits/stdc++.h>
                    using namespace std;
                    
                    //returns answer
                    int solution(vector<int> arr, int src, int dest){
                        // Two maps, visA for distance from src and visB for distance from dest
                        // They serve two purpose, if visA[x] == 0, that means we haven't reached that node yet, 
                        // and if it holds any value, say d, that means it is d distance away from the particular node
                        map<int,int> visA,visB;
                        int start = arr[src];
                        int curr = 1;
                        set<int> s; // contains unique set of nodes to check at last
                        
                        // iniitializing final nodes
                        for(auto &x: arr){
                            s.insert(x);
                        }
                        // traversing until we get to a cell where we've already reached
                        while(visA[start] == 0){
                            visA[start] = curr; // Marcking the distance
                            curr++;
                            start = arr[start];
                            if(start == -1){
                                break; // Getting out if we get to a node who is not pointing at any other node
                            }
                        }
                        start = arr[dest];
                        // Same logic as above but traversing from dest
                        while(visB[start] == 0){
                            visB[start] = curr;
                            curr++;
                            start = arr[start];
                            if(start == -1){
                                break;
                            }
                        }
                        // This is an array of two values, vp[i].first holds the sum of distance of vp[i].second from src and dest.
                        vector<pair<int,int>> vp;
                        for(auto &x: s){
                            if(visA[x] != 0 && visB[x] != 0){ // Checking if we ever got to that particular node from both src and dest or not
                                pair<int,int> p = {visA[x] + visB[x], x};
                                vp.push_back(p);
                            }
                        }
                        // sorting and finding the node with list sum of visA[} + visB[]
                        sort(vp.begin(), vp.end());
                        return vp[0].second;
                    }
                    
                    int main(){
                        int n; cin>>n;
                        vector<int> v;
                        for(int i = 0; i<n; i++){
                            int a; cin>>a;
                            v.push_back(a);
                        }
                        int a,b; cin>>a>>b;
                        cout << (solution(v,a,b));
                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-03-24
                      • 2011-09-22
                      相关资源
                      最近更新 更多