1

I am trying to come up with a greedy algorithm in Python that returns the vertices in an undirected graph given a certain starting vertex. I understand that DFS determines if a cycle exists, but I am trying to actually return the vertices that form the cycle. I am using an adjacency matrix to represent the following graph:

adjacencyMatrix = [[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]

pictorially this is an undirected graph comprised of a single cycle.

My current thought process is to set my starting index to the first 1 I come across (In this case adjacencyMatrix[0][1]). Then I would look at the rest of the row to see if another 1 was there, because that means my current vertex is connected to that index. However, I am not entirely sure if (a) this is the right approach and (b) how to "move" to the next vertex. For example, how would I navigate my nested for loop to move from the adjacencyMatrix[0][1] vertex to the adjacencyMatrix[0][2] vertex? Would I just swap the row and column indices?

EDIT This solution I came up with seems to work for the few graphs I tried it on:

def findCycle(matrix):
    visited = list()
    cycleNotFound = True
    row = 0
    col = 0
    startVertex = (0, 0)

    while cycleNotFound:

        # Only add a vertex if it has not already been visited
        if (matrix[row][col] == 1) and ((row, col) not in visited):
            # Set the startVertex when the first node is found
            if len(visited) == 0:
                startVertex = (row, col)

            # Add the current vertex and its counter part
            visited.append((row, col))
            visited.append((col, row))

            # If row and col are equal, infite loop will get created
            if row != col:
                row = col
                col = 0
            else:
                row += 1

        # If back at starting point, break look
        elif ((row, col) == startVertex) and (len(visited) > 1):
            cycleNotFound = False
            visited.append(startVertex)

        # Else, continue to look for unvisted neighbors
        else:
            col += 1

    return visited

if __name__ == "__main__":
    matrix = [[0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]]
    cycle = findCycle(matrix)
    index = 0
    # Print the vertices.  Only print even vertices to avoid duplicates.
    while (index < len(cycle)):
        print cycle[index]
        index += 2

it is not the most elegant solution and I'm sure there is some major refactoring that needs to be done.

steveclark
  • 537
  • 9
  • 27
  • If you're doing DFS you go to one of your neighbors recursively, after finishing DFS from this neighbor you move on to the next neighbors etc. – Nir Alfasi May 04 '15 at 05:43
  • Okay, so would I just first run DFS to determine if a cycle exists and then use a slightly modified DFS method to return all of the visited vertices? – steveclark May 04 '15 at 05:47
  • First of, the adjacency matrix is symmetrical. The number of vertices is defined by the size of the matrix. A matrix of size `n x n` means that there are `n` vertices. To list vertices connected to a certain vertex check the corresponding row for ones, their indices "are" the vertices you are looking for. – PeterE May 04 '15 at 05:52

1 Answers1

2

You may try this:

def findCycle(node):
    cycle = stack()
    if( DFS(node, cycle) ):
        return cycle
    return None

def DFS(node, cycle):
    cycle.append(node)
    mark node as visited
    foreach node.neighbors as neighbor:
        if neighbor already visited:
            return true
        else:
            if( DFS(neighbor, cycle) ) return true
    cycle.remove(node)
    return false
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
Toby D
  • 1,465
  • 1
  • 19
  • 31
  • Okay, this actually might be similar to what I am currently working on. I assume the `neighbors` function would return a list of the column numbers for each `[row][col] == 1`? For instance using the provided adjacency matrix `1.neighbors` would return `[2, 3]`? – steveclark May 04 '15 at 06:25
  • @steveclark hi, please choose my answer if you find it correct. Thx – Toby D May 04 '15 at 07:09
  • I wasn't able to implement your solution and since I suck at debugging recursion I decided to continue working on the one I was already working on. I posted the code for my solution in the question as an edit. – steveclark May 04 '15 at 08:01
  • @TobyD: I think `findCycle` can return the wrong nodes. `DFS` doesn't add the already-marked node to `cycle` (although it could be found among the top node's neighbors, hopefully still marked). Worse, it includes nodes in `cycle` that _aren't_ part of the cycle. Consider a cycle A->B->C->B: The nodes popped off of `cycle` will be: C, B, A. If `DFS` pushed B onto `cycle` before returning `True`, `findCycle` could pop and `yield` B, and then continue popping and `yield`ing until B showed up again. But without the "guilty" node on top of the stack, `findCycle` has some sleuthing to do. – Kevin J. Chase May 04 '15 at 08:36