如何为迭代深度优先搜索添加完成时间?

时间:2015-03-05 13:56:04

标签: python algorithm graph-theory depth-first-search

我正在尝试创建depth-first算法来分配完成时间(顶点无法再展开的时间),这些算法用于Kosaraju's algorithm之类的内容。我能够相当容易地创建DFS的递归版本,但是我很难将其转换为迭代版本。

我正在使用邻接列表来表示图形:顶点的字典。例如,输入图{1: [0, 4], 2: [1, 5], 3: [1], 4: [1, 3], 5: [2, 4], 6: [3, 4, 7, 8], 7: [5, 6], 8: [9], 9: [6, 11], 10: [9], 11: [10]}表示边(1,0),(1,4),(2,1),(2,5)等。以下是使用的迭代DFS的实现一个简单的堆栈(LIFO),但它不计算完成时间。我遇到的一个关键问题是,由于顶点被弹出,一旦顶点完全展开,算法就无法追溯其路径(与递归不同)。我该如何解决这个问题?

def dfs(graph, vertex, finish, explored):
    global count

    stack = []
    stack.append(vertex)

    while len(stack) != 0:
        vertex = stack.pop()

        if explored[vertex] == False:
            explored[vertex] = True

            #add all outgoing edges to stack:
            if vertex in graph:                 #check if key exists in hash -- since not all vertices have outgoing edges
                for v in graph[vertex]:
                    stack.append(v)

        #this doesn't assign finishing times, it assigns the times when vertices are discovered:                                            
        #finish[count] = vertex
        #count += 1

N.b。还有一个外部循环补充了DFS - 但是,我不认为问题出在那里:

#outer loop:
for vertex in range(size, 0, -1):
    if explored[vertex] == False:
        dfs(hGraph, vertex, finish, explored)

3 个答案:

答案 0 :(得分:6)

将您的堆栈视为一堆任务,而不是顶点。您需要执行两种类型的任务。您需要扩展顶点,并且需要添加完成时间。

当您扩展顶点时,首先添加计算完成时间的任务,然后添加扩展每个子顶点。

当您添加完成时间时,您可以知道扩展已完成。

答案 1 :(得分:1)

这是一个在迭代子例程中使用两个堆栈的工作解决方案。数组traceBack包含已经探索过的顶点,并与互补的2D数组stack相关联,该数组包含尚未被探索的边数组。这两个数组是相互关联的;当我们向traceBack添加元素时,我们也会添加stack(与弹出元素相同)。

count = 0
def outerLoop(hGraph, N):
    explored    = [False for iii in range(N+1)]
    finish      = {}

    for vertex in range(N, -1, -1):
        if explored[vertex] == False:
            dfs(vertex, hGraph, explored, finish)

    return finish


def dfs(vertex, graph, explored, finish):
    global count
    stack       = [[]]                              #stack contains the possible edges to explore
    traceBack   = []
    traceBack.append(vertex)

    while len(stack) > 0:
        explored[vertex] = True

        try:
            for n in graph[vertex]:
                if explored[n] == False:
                    if n not in stack[-1]:          #to prevent double adding (when we backtrack to previous vertex)
                        stack[-1].append(n)
                else:
                    if n in stack[-1]:              #make sure num exists in array before removing
                        stack[-1].remove(n)             
        except IndexError: pass

        if len(stack[-1]) == 0:                     #if current stack is empty then there are no outgoing edges:
            finish[count] = traceBack.pop()         #thus, we can add finishing times
            count += 1

            if len(traceBack) > 0:                  #to prevent popping empty array
                vertex = traceBack[-1]
            stack.pop()
        else:
            vertex = stack[-1][-1]                  #pick last element in top of stack to be new vertex

            stack.append([])
            traceBack.append(vertex)

答案 2 :(得分:0)

这是一种方式。每当我们遇到以下情况时,我们会进行回调或标记时间,

  • 节点没有传出边缘(无路径)。
  • 当遍历节点的父节点与上一个父节点(父节点更改)不同时。在那种情况下,我们完成了最后一位父母。
  • 当我们到达堆栈的末尾时(树端)。我们完成了最后一位家长。

这是代码,

var dfs_with_finishing_time = function(graph, start, cb) {
    var explored = [];
    var parent = [];
    var i = 0;
    for(i = 0; i < graph.length; i++) {
            if(i in explored)
                    continue;
            explored[i] = 1;
            var stack = [i];
            parent[i] = -1;
            var last_parent = -1;
            while(stack.length) {
                    var u = stack.pop();
                    var k = 0;
                    var no_way = true;
                    for(k = 0; k < graph.length; k++) {
                            if(k in explored)
                                    continue;
                            if(!graph[u][k])
                                    continue;
                            stack.push(k);
                            explored[k] = 1;
                            parent[k] = u;
                            no_way = false;
                    }
                    if(no_way) {
                            cb(null, u+1); // no way, reversed post-ordering (finishing time)
                    }
                    if(last_parent != parent[u] && last_parent != -1) {
                            cb(null, last_parent+1); // parent change, reversed post-ordering (finishing time)
                    }
                    last_parent = parent[u];
            }
            if(last_parent != -1) {
                    cb(null, last_parent+1); // tree end, reversed post-ordering (finishing time)
            }
    }
}