【问题标题】:Topological sort python拓扑排序python
【发布时间】:2013-02-23 09:06:00
【问题描述】:

我为 DFS 非递归编写了一个解决方案,但我无法修改它以进行拓扑排序:

def dfs(graph,start):
    path = []
    stack = [start]    
    while stack != []: 
        v = stack.pop()
        if v not in path: path.append(v)
        for w in reversed(graph[v]): 
            if w not in path and not w in stack:
                stack.append(w) 
    return path

任何想法如何修改它?

使用递归版本,我可以轻松进行排序:

def dfs_rec(graph,start,path):
    path = path + [start]
    for edge in graph[start]: 
        if edge not in path:
            path = dfs_rec(graph, edge,path)
    print start
    return path

输入:

>>> graph = {
        1: [2, 3],
        2: [4, 5, 6],
        3: [4,6],
        4: [5,6],
        5: [6],
        6: []
    }
>>> dfs_rec(graph,1,[])
6
5
4
2
3
1
[1, 2, 4, 5, 6, 3]
>>> dfs(graph,1)
[1, 2, 4, 5, 6, 3]
>>> graph = {
        1: [3],
        3: [5,6],
        5: [4],
        4: [7],
        7: [],
        6: []
    }
>>> print dfs_rec(graph,1,[])
7
4
5
6
3
1
[1, 3, 5, 4, 7, 6]
>>> print dfs(graph,1)
[1, 3, 5, 4, 7, 6]

所以我也需要在非递归中得到这个排序。

非递归解:

我认为这也可能是解决方案,如果我错了,请标记我。

def dfs(graph,start):
    path = []
    stack = [start]
    label = len(graph)
    result = {}  
    while stack != []:
        #this for loop could be done in other ways also
        for element in stack:
            if element not in result:
                result[element] = label
                label = label - 1

        v = stack.pop()
        if v not in path: path.append(v)
        for w in reversed(graph[v]): 
            if w not in path and not w in stack:
                stack.append(w) 

    result = {v:k for k, v in result.items()}
    return path,result

输入:

graph = { 1: [3], 3:[5,6] , 5:[4] , 4:[7], 7:[],6:[]}
print dfs(graph,1) 

输出:

([1, 3, 5, 4, 7, 6], {1: 7, 2: 4, 3: 5, 4: 6, 5: 3, 6: 1})

        1
       / 
      3
     /\
    5  6
   /
  4
 /
7    

【问题讨论】:

  • 你能举个输入的例子吗?
  • 这里有什么问题?在这两种情况下,dfs_rec 的返回值都与dfs 的返回值相匹配。你想要什么结果?
  • 是的结果匹配,但是在递归结束时在 dfs_rec 中它给了我(通过打印开始)图的拓扑排序,所以现在我想对非递归进行拓扑排序功能(dfs),但我无法成功。
  • 那么你希望两个函数在第二种情况下都返回[7, 4, 5, 6, 3, 1]
  • [6, 5, 4, 2, 3, 1] 和 [7, 4, 5, 6, 3, 1] 用于 dfs(graph,1) 非递归函数。如您所见,我已经为第一个函数 dfs_rec 提供了它。

标签: python algorithm graph


【解决方案1】:

FWIW,这是我为非递归拓扑排序编写的一些代码。

from collections import defaultdict, namedtuple
from itertools import islice

Results = namedtuple('Results', ['sorted', 'cyclic'])

def topological_sort(dependency_pairs):
    'Sort values subject to dependency constraints'
    num_heads = defaultdict(int)   # num arrows pointing in
    tails = defaultdict(list)      # list of arrows going out
    heads = []                     # unique list of heads in order first seen
    for h, t in dependency_pairs:
        num_heads[t] += 1
        if h in tails:
            tails[h].append(t)
        else:
            tails[h] = [t]
            heads.append(h)

    ordered = [h for h in heads if h not in num_heads]
    for h in ordered:
        for t in tails[h]:
            num_heads[t] -= 1
            if not num_heads[t]:
                ordered.append(t)
    cyclic = [n for n, heads in num_heads.items() if heads]
    return Results(ordered, cyclic)

if __name__ == '__main__':
    print( topological_sort('aa'.split()) )
    print( topological_sort('ah bg cf ch di ed fb fg hd he ib'.split()) )

【讨论】:

    【解决方案2】:
    from collections import defaultdict, deque
    
    
    class Graph:
        def __init__(self, directed=False, nodes=None, edges=None):
            self.graph = defaultdict(list)
            self.directed = directed
            self.add_nodes(nodes)
            self.add_edges(edges)
    
        @property
        def nodes(self):
            if not self.directed:
                return list(self.graph.keys())
            elif self.directed:
                nodes = set()
                nodes.update(self.graph.keys())
                for node in self.graph.keys():
                    for neighbor in self.graph[node]:
                        nodes.add(neighbor)
                return list(nodes)
    
        def add_node(self, node):
            if node not in self.nodes:
                self.graph[node] = list()
    
        def add_nodes(self, nodes):
            if nodes is None:
                return None
            for node in nodes:
                self.add_node(node)
    
        @property
        def edges(self):
            edges = list()
            for source, neighbors in self.graph.items():
                for neighbor in neighbors:
                    edges.append((source, neighbor))
            return edges
    
        def add_edge(self, edge):
            node1, node2 = edge
            self.graph[node1].append(node2)
            if not self.directed:
                self.graph[node2].append(node1)
    
        def add_edges(self, edges):
            if edges is None:
                return None
            for edge in edges:
                self.add_edge(edge)
    
        def topological_util(self, node, visited, label):
            visited[node] = True
            for edge in self.graph[node]:
                if not visited[edge]:
                    self.topological_util(edge, visited, label)
            label.appendleft(node)
    
        def topological_sort(self):
            visited = dict.fromkeys(self.nodes, False)
            # store all nodes in topological order, the index is the position
            label = deque()
            for node in self.nodes:
                if not visited[node]:
                    self.topological_util(node, visited, label)
            return label
    

    【讨论】:

      【解决方案3】:
          #this algorithm gives the logic of topological sorting..if u want to run this 
          #give adjacency mat of your choice and this algorithm works on graph elements ranging from 0 to n 
      
          a=[[0,0,1,0,0,0],[0,0,1,0,0,0],[0,0,0,1,1,0],[0,0,0,0,1,0],[0,0,0,0,0,0],[0,0,1,0,0,0]]
          vis=[0 for i in range(0,len(a))]
          s=[]
      
          orderstack=[]#stores the reverse order of topological sorted elements
      
          def dfs_for_topological_sorting(a,vis,i):
      
              vis[i]=1
              x=0
              for j in range(0,len(a[0])):
                  if(a[i][j]==1 and vis[j]==0):
                      x=1
                      s.append(j)
                      #print(s)
                      dfs_for_topological_sorting(a,vis,j)
              if(x==0 and len(s)!=0):
      
                  orderstack.append(s[len(s)-1])
              if(len(s)>0):
                  dfs_for_topological_sorting(a,vis,s.pop())
      
          for i in range(0,len(a)):
              if(i not in orderstack):
                  s.append(i)
                  dfs_for_topological_sorting(a,vis,i)
          print(orderstack[len(orderstack)-1::-1])        
      

      【讨论】:

        【解决方案4】:
            from collections import defaultdict  # importing defaultdict      
            def topological_sort(graph,b,a):     # defining function
                T = []
                visited = []
                in_degree = []
                for i in range(a+1):            
                        in_degree.append(0)     # initialising the degree of each vertex =0
                        visited.append(0)       # initialising all the vertics unvisited
                for i in range(1,a+1):
                        for j in graph[i]:
                            in_degree[j] = in_degree[j] + 1  # now assigning and incrementing 
                Queue=[]                                     # the degree of each vertex acc.
                for i in range(1,a+1):
                        if in_degree[i]==0:
                                Queue.append(i)         # appending those vertices which have zero 
                                visited[i] = 1          # degree and making it as visited   
                while Queue :
                        vertex = Queue.pop(Queue.index(min(Queue))) # popping each element in 
                        T.append(vertex)                            # lexicographical order and 
                        for j in graph[vertex]:                     # appending to T
                                if visited[j]==0:
                                    in_degree[j] = in_degree[j] - 1
                                    if in_degree[j] == 0:          
                                            Queue.append(j)       #according to each popped vertex
                                            visited[j] = 1        #as key in graph check whether
                return T                                          #in list corresponding to key 
                                                                  # as value,is it not visited and 
                                                                  #decreasing its value when it 
                                                                  #becomes zero,append it to queue
                                                                  #and mark it as visited
            graph=defaultdict(list)    
            a,b=list(map(int,input().split())) #a=no. of vertices  
            for i in range(b):                 #b=no. of edges
                p,q=list(map(int,input().split())) 
                graph[p].append(q)      # we take input in graph as DAG
            ss=topological_sort(graph,b,a) # calling function
            for i in ss:
                print(i,end=" ")
        '''Input
            5 6
            1 2
            1 3
            2 3
            2 4
            3 4
            3 5
          Your Code's Output
            1 2 3 4 5
          Expected Correct Output
            1 2 3 4 5 '''
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-07-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多