Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Traverse a graph represented in an adjacency matrix

I am wanting to use the depth-first and breadth-first methods for traversing a graph. I have done this on a simple list of nodes before, but I have never tried it with an adjacency matrix and I honestly don't even know where to start.

Here is my matrix:

999999999 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 
1 999999999 0 3 1 0 0 0 0 0 0 0 0 0 0 0 
1 0 999999999 3 0 1 0 0 0 0 0 0 0 0 0 0 
0 3 3 999999999 0 0 0 8 0 0 0 0 0 0 0 0 
0 1 0 0 999999999 0 1 3 0 0 0 0 0 0 0 0 
0 0 1 0 0 999999999 0 3 1 0 0 0 0 0 0 0 
0 0 0 0 1 0 999999999 0 0 3 0 1 0 0 0 0 
0 0 0 8 3 3 0 999999999 0 8 8 0 3 0 0 0 
0 0 0 0 0 1 0 0 999999999 0 3 0 0 1 0 0 
0 0 0 0 0 0 3 8 0 999999999 0 0 0 0 3 0 
0 0 0 0 0 0 0 8 3 0 999999999 0 0 0 0 3 
0 0 0 0 0 0 1 0 0 0 0 999999999 0 0 1 0 
0 0 0 0 0 0 0 3 0 0 0 0 999999999 0 1 1 
0 0 0 0 0 0 0 0 1 0 0 0 0 999999999 0 1 
0 0 0 0 0 0 0 0 0 3 0 1 1 0 999999999 0 
0 0 0 0 0 0 0 0 0 0 3 0 1 1 0 999999999

Here is how I created this matrix (C#):

private static int[,] CreateMatrix()
        {
            int A = 0;
            int B = 1;
            int C = 2;
            int D = 3;
            int E = 4;
            int F = 5;
            int G = 6;
            int H = 7;
            int I = 8;
            int J = 9;
            int K = 10;
            int L = 11;
            int M = 12;
            int N = 13;
            int O = 14;
            int P = 15;

            int[,] matrix = new int[16, 16];

            matrix[A, B] = 1;
            matrix[A, C] = 1;
            matrix[B, D] = 3;
            matrix[B, E] = 1;
            matrix[C, D] = 3;
            matrix[C, F] = 1;
            matrix[D, H] = 8;
            matrix[E, G] = 1;
            matrix[E, H] = 3;
            matrix[F, H] = 3;
            matrix[F, I] = 1;
            matrix[G, J] = 3;
            matrix[G, L] = 1;
            matrix[H, J] = 8;
            matrix[H, K] = 8;
            matrix[H, M] = 3;
            matrix[I, K] = 3;
            matrix[I, N] = 1;
            matrix[J, O] = 3;
            matrix[K, P] = 3;
            matrix[L, O] = 1;
            matrix[M, O] = 1;
            matrix[M, P] = 1;
            matrix[N, P] = 1;


            matrix[B, A] = 1;
            matrix[C, A] = 1;
            matrix[D, B] = 3;
            matrix[E, B] = 1;
            matrix[D, C] = 3;
            matrix[F, C] = 1;
            matrix[H, D] = 8;
            matrix[G, E] = 1;
            matrix[H, E] = 3;
            matrix[H, F] = 3;
            matrix[I, F] = 1;
            matrix[J, G] = 3;
            matrix[L, G] = 1;
            matrix[J, H] = 8;
            matrix[K, H] = 8;
            matrix[M, H] = 3;
            matrix[K, I] = 3;
            matrix[N, I] = 1;
            matrix[O, J] = 3;
            matrix[P, K] = 3;
            matrix[O, L] = 1;
            matrix[O, M] = 1;
            matrix[P, M] = 1;
            matrix[P, N] = 1;

            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 16; j++)
                {
                    if (matrix[i, j] == 0)
                        matrix[i, j] = 0;

                    if (i == j)
                        matrix[i, j] = 999999999;

                }
            }

            return matrix;
        }

Any help would be appreciated!!

Graph that this matrix represents:

enter image description here

like image 701
JLott Avatar asked Nov 30 '22 21:11

JLott


1 Answers

Every problem in computer science except one can be solved by adding more abstraction.

Start by writing the breadth-first-traversal in the most abstract way possible:

void BreadthFirstTraversal(Graph graph, Vertex start)
{
    /* A miracle happens */
}

We have a method that does what we want. Except that it hasn't been written yet. So write it with slightly less abstraction:

void BreadthFirstTraversal(Graph graph, Vertex start)
{
    /* make a queue of vertices */
    /* make a mark set of vertices */
    /* enqueue and mark start */
    /* while the queue is not empty */
        /* dequeue a vertext */
        /* enqueue and mark all the unmarked neighbours of the vertex */
}

Keep on going, removing more and more abstraction.

void BreadthFirstTraversal(Graph graph, Vertex start)
{
    var queue = new VertexQueue();
    var markSet = new VertexMarkSet();
    queue.Enqueue(start);
    markSet.Add(start);
    while(queue.NotEmpty())
    {
        var vertex = queue.Dequeue();
        foreach(Vertex neighbour in graph.ListNeighbours(vertex))
        {
            if (!markSet.Contains(neighbour))
            {
                markSet.Add(neighbour);
                queue.Enqueue(neighbour);
            }
        }
     }
}

OK, now you've got an algorithm that will work for any graph, no matter what its representation internally. So all you have to do is write ListNeighbours(Vertex) and you're done. (Assuming that you already know how to write a queue and a set, or are willing to use the types that come with the base class library.) How are you going to do that?

You see how I used abstraction there? I really don't care whether its an adjacency matrix or an adjacency list, all I care about is that the graph provides me the service of giving me the neighbours of a vertex.

So, how are you going to write ListNeighbours(Vertex) given your adjacency matrix?

Two possible solutions to investigate:

  • Make the Graph.ListNeighbours(Vertex) method return a List<Vertex>. Construct the list and hand it out.

  • Make it return IEnumerable<Vertex> and use yield return to yield a sequence of neighbouring vertices.


UPDATE: OK, so how do we actually make a sequence of neighbours out of an adjacency matrix?

Let's suppose that every vertex is numbered, so Vertex is really int; this is traditionally how things are done with adjacency matrices. We want to take in a vertex -- an int -- and return a sequence of vertices that are its neighbours.

We have an array that has the property that array[i, j] is non-zero if vertex j is a neighbour of vertex i.

Again, start abstract and work your way towards implementation:

public List<int> ListNeighbours(int vertex)
{
    /* a miracle happens */
}

What do we need to do to make that miracle happen?

public List<int> ListNeighbours(int vertex)
{
    /* create a new list */
    /* for each vertex j in the graph */
        /* if j is a neighbour of i then add it to the list */
    /* return the list */
}

Alternatively, you can use yield return to create a sequence:

public IEnumerable<int> ListNeighbours(int vertex)
{
    /* for each vertex j in the graph */
        /* if j is a neighbour of i then yield return j */
}

yield return iterators tend to be simpler, but beginner programmers often have a hard time sussing out the control flow. Try writing it both ways and see how it goes.

like image 69
Eric Lippert Avatar answered Dec 20 '22 11:12

Eric Lippert