Топологическая сортировка с простым вводом приводит к «IndexError: индекс списка вне диапазона»

#python #list #topological-sort

#python #Список #топологическая сортировка

Вопрос:

Я изучал топологическую сортировку и наткнулся на код Python GeeksforGeek.

 # Python program to print topological sorting of a DAG 
from collections import defaultdict 
  
# Class to represent a graph 
class Graph: 
    def __init__(self,vertices): 
        self.graph = defaultdict(list) # dictionary containing adjacency List 
        self.V = vertices #No. of vertices 
  
    # function to add an edge to graph 
    def addEdge(self,u,v): 
        self.graph[u].append(v) 
  
    # A recursive function used by topologicalSort 
    def topologicalSortUtil(self,v,visited,stack): 
  
        # Mark the current node as visited. 
        visited[v] = True
  
        # Recur for all the vertices adjacent to this vertex 
        for i in self.graph[v]: 
            if visited[i] == False: 
                self.topologicalSortUtil(i,visited,stack) 
  
        # Push current vertex to stack which stores result 
        stack.insert(0,v) 
  
    # The function to do Topological Sort. It uses recursive  
    # topologicalSortUtil() 
    def topologicalSort(self): 
        # Mark all the vertices as not visited 
        visited = [False]*self.V 
        stack =[] 
  
        # Call the recursive helper function to store Topological 
        # Sort starting from all vertices one by one 
        for i in range(self.V): 
            if visited[i] == False: 
                self.topologicalSortUtil(i,visited,stack) 
  
        # Print contents of stack 
        print stack 

# Driver Code
g = Graph(6) 
g.addEdge(5, 2)
g.addEdge(5, 0) 
g.addEdge(4, 0) 
g.addEdge(4, 1) 
g.addEdge(2, 3) 
g.addEdge(3, 1) 
  
print "Following is a Topological Sort of the given graph"
g.topologicalSort() 
  

Однако я изменил входные данные, потому что хотел протестировать реальный случай, который мне нужен для моего проекта.

 g = Graph(5) 

g.addEdge(1, 2); 
g.addEdge(2, 3); 
g.addEdge(3, 4); 
g.addEdge(4, 5); 
  

Но я получаю исключение «индекс списка вне диапазона» в рекуррентной функции и не могу понять, почему.

Трассировка стека

 IndexError                                Traceback (most recent call last)
<ipython-input-8-cb05f6defc6c> in <module>
     54 
     55 print("Following is a Topological Sort of the given graph")
---> 56 g.topologicalSort()

<ipython-input-8-cb05f6defc6c> in topologicalSort(self)
     37         for i in range(self.V):
     38             if visited[i] == False:
---> 39                 self.topologicalSortUtil(i,visited,stack)
     40 
     41         # Print contents of stack

<ipython-input-8-cb05f6defc6c> in topologicalSortUtil(self, v, visited, stack)
     21         for i in self.graph[v]:
     22             if visited[i] == False:
---> 23                 self.topologicalSortUtil(i,visited,stack)
     24 
     25         # Push current vertex to stack which stores result

<ipython-input-8-cb05f6defc6c> in topologicalSortUtil(self, v, visited, stack)
     21         for i in self.graph[v]:
     22             if visited[i] == False:
---> 23                 self.topologicalSortUtil(i,visited,stack)
     24 
     25         # Push current vertex to stack which stores result

<ipython-input-8-cb05f6defc6c> in topologicalSortUtil(self, v, visited, stack)
     21         for i in self.graph[v]:
     22             if visited[i] == False:
---> 23                 self.topologicalSortUtil(i,visited,stack)
     24 
     25         # Push current vertex to stack which stores result

<ipython-input-8-cb05f6defc6c> in topologicalSortUtil(self, v, visited, stack)
     20         # Recur for all the vertices adjacent to this vertex
     21         for i in self.graph[v]:
---> 22             if visited[i] == False:
     23                 self.topologicalSortUtil(i,visited,stack)
     24 

IndexError: list index out of range
  

Комментарии:

1. Опубликуйте трассировку стека.

Ответ №1:

Это IndexError: list index out of range вызвано тем, что в этом примере кода предполагается, что вершины нумеруются, начиная с нуля.

Список visited инициализируется как:

 visited = [False]*self.V 
  

Следовательно, если граф инициализирован с 5 вершинами, то индексы visited будут 0, 1, 2, 3, 4.

Затем позже функция topologicalSortUtil(self, v, visited, stack) выполняет строку 22 ниже, и вершина 5 выходит за пределы диапазона, поскольку диапазон варьируется от 0 до 4:

      21    for i in self.graph[v]:
---> 22        if visited[i] == False:
     23            self.topologicalSortUtil(i,visited,stack)
  

Следующий код, использующий индексацию вершин на основе нуля, работает так, как ожидалось.

 g = Graph(5) 

g.addEdge(0, 1)
g.addEdge(1, 2) 
g.addEdge(2, 3) 
g.addEdge(3, 4)

print("Following is a Topological Sort of the given graph")
g.topologicalSort() 
  

Вывод:

 Following is a Topological Sort of the given graph
[0, 1, 2, 3, 4]
  

Реализация топологической сортировки, которая работает с произвольными номерами вершин

Следующая реализация не требует от вас указания количества вершин, поскольку количество автоматически увеличивается всякий раз, когда новая вершина добавляется в граф с помощью add_edge метода.

Кроме того, вершины не обязательно должны начинаться с какого-либо определенного числа. Я пошел дальше и очистил форматирование, чтобы использовать стандартные соглашения об именах в змеином регистре.

 from collections import defaultdict 
  
class Graph: 
    def __init__(self): 
        self.graph = defaultdict(list) #dictionary containing adjacency List 
  
    def add_edge(self,u,v): 
        self.graph[u].append(v) 
  
    def topological_sort(self) -> list: 
        visited = set()
        reverse_topo = list() 
  
        vertices = set(self.graph.keys())
        for vertex in vertices: 
            if vertex not in visited:
                self._topological_sort_util(vertex, visited, reverse_topo) 
        return list(reversed(reverse_topo))
    
    def _topological_sort_util(self, vertex, visited, reverse_topo): 
        visited.add(vertex)

        for adj_vertex in self.graph[vertex]: 
            if adj_vertex not in visited: 
                self._topological_sort_util(adj_vertex, visited, reverse_topo) 
        reverse_topo.append(vertex)


g = Graph() 
g.add_edge(1, 2)
g.add_edge(2, 3) 
g.add_edge(3, 4) 
g.add_edge(4, 5) 
  
print("Following is a Topological Sort of the given graph")
vertices_topo_sorted = g.topological_sort() 
print(vertices_topo_sorted)
  

Вывод

 Following is a Topological Sort of the given graph
[1, 2, 3, 4, 5]
  

Комментарии:

1. В моем конкретном проекте входные данные должны начинаться с 1, поскольку они представляют номера документов. Что я мог бы изменить, чтобы разрешить это?

2. @Johnny См. Обновленный ответ с новой версией топологической сортировки, которая работает с произвольными номерами вершин.