COMP9024 23T3
Dr. Aditya Joshi
Week 4 Problem Set
Graph Data Structures and Graph Search

[Show with no answers]   [Show with all answers]


  1. (Graph fundamentals)

    For the graph

    give examples of the smallest (but not of size/length 0) and largest of each of the following:

    1. simple path
    2. cycle
    3. spanning tree
    4. vertex degree
    5. clique

    [show answer]


  2. (Graph properties)
    1. Write pseudocode for computing

      • the degree of each vertex
      • all 3-cliques (i.e. cliques of 3 nodes, "triangles")
      • and the density

      of an undirected graph g with n vertices.

      Your methods should be representation-independent; the only function you should use is to check if two vertices v,w∈{0,…n-1} are adjacent in g.

      The density is the fraction of edges present over all possible edges, which, for an undirected graph with `|E|` edges and `|V|` vertices, is defined as `(2|E|)/(|V|(|V|-1))`.

    2. Determine the asymptotic complexity of your three algorithms. Assume that the adjacency check is performed in constant time, O(1).

    3. Implement your algorithms in a program graphAnalyser.c that

      1. builds a graph from user input:
        • first, the user is prompted for the number of vertices
        • then, the user is repeatedly asked to input an edge by entering a "from" vertex followed by a "to" vertex
        • until any non-numeric character(s) are entered
      2. outputs the degree of each vertex, considering the vertices in ascending order, i.e. starting with the degree of vertex 0, followed by the degree of vertex 1 and so on;
      3. displays all 3-cliques of the graph in ascending order;
      4. displays the density of the graph rounded to three decimal places.

      Your program should use the Graph ADT from the lecture (Graph.h and Graph.c). These files should not be changed.

      Hint: You may assume that the graph has a maximum of 1000 nodes.

      We have also provided a Makefile to simplify the compilation process. Place it in the same directory as your code and simply execute:

      make

      If you'd like to delete the object files it creates, you can instead execute:

      make clean

      An example of the program executing is shown below for the graph


      ./graphAnalyser
      Enter the number of vertices: 6
      Enter an edge (from): 0
      Enter an edge (to): 1
      Enter an edge (from): 1
      Enter an edge (to): 2
      Enter an edge (from): 4
      Enter an edge (to): 2
      Enter an edge (from): 1
      Enter an edge (to): 3
      Enter an edge (from): 3
      Enter an edge (to): 4
      Enter an edge (from): 1
      Enter an edge (to): 5
      Enter an edge (from): 5
      Enter an edge (to): 3
      Enter an edge (from): 2
      Enter an edge (to): 3
      Enter an edge (from): done
      Done.
      Vertex degrees:
      1, 4, 3, 4, 2, 2
      Triplets:
      1-2-3
      1-3-5
      2-3-4
      Density: 0.533
      

      Note that any non-numeric data can be used to 'finish' the interaction.

    We have created a script that can automatically test your program. To run this test you can execute the dryrun program that corresponds to this exercise. It expects to find a program named graphAnalyser.c in the current directory. You can use dryrun as follows:

    9024 dryrun graphAnalyser
    

    Please ensure that your program output follows exactly the format shown in the sample interaction above. In particular, the degree should be shown for the vertices in ascending order, the 3-cliques should be printed in ascending order, and the density should be printed to three decimal places.

    [show answer]


  3. (Graph representations)
    1. Show how the following graph would be represented by

      1. an adjacency matrix representation (V×V matrix with each edge represented twice)
      2. an adjacency list representation (where each edge appears in two lists, one for v and one for w)
    2. Consider the adjacency matrix and adjacency list representations for graphs. Analyse the storage costs for the two representations in more detail in terms of the number of vertices V and the number of edges E. Determine the E:V ratio at which it is more storage efficient to use an adjacency matrix representation vs the adjacency list representation.

      Assumptions. For the purposes of the analysis, ignore the cost of storing the GraphRep structure. Assume that: each pointer is 8 bytes long, a Vertex value is 4 bytes, a linked-list node is 16 bytes long, the adjacency matrix is a complete V×V matrix. Assume also that each adjacency matrix element is 1 byte long. (Hint: Defining the matrix elements as 1-byte boolean values rather than 4-byte integers is a simple way to improve the space efficiency for the adjacency matrix representation.)

    3. The standard adjacency matrix representation for a graph uses a full V×V matrix and stores each edge twice (at [v,w] and [w,v]). This consumes a lot of space, and wastes a lot of space when the graph is sparse. One way to use less space is to store just the upper (or lower) triangular part of the matrix, as shown in the diagram below:

      The V×V matrix has been replaced by a single 1-dimensional array g.edges[] containing just the "useful" parts of the matrix.

      Accessing the elements is no longer as simple as g.edges[v][w]. Write pseudocode for a method to check whether two vertices v and w are adjacent under the upper-triangle matrix representation of a graph g.

    [show answer]


  4. (Graph traversal: DFS and BFS)

    Both DFS and BFS can be used for a complete search without a specific destination, which means traversing a graph until all reachable nodes have been visited. Show the order in which the nodes of the graph depicted below are visited by

    1. DFS starting at node 7
    2. DFS starting at node 4
    3. BFS starting at node 7
    4. BFS starting at node 4

    Assume the use of a stack for depth-first search (DFS) and a queue for breadth-first search (BFS), respectively, and use the pseudocode from the lecture: DFS, BFS. Show the state of the stack or queue explicitly in each step. When choosing which neighbour to visit next, always choose the smallest unvisited neighbour.

    [show answer]


  5. (Cycle check)
    1. Take the "buggy" cycle check from the lecture and design a correct algorithm, in pseudocode, to use depth-first search to determine if a graph has a cycle.

    2. Write a C program cycleCheck.c that implements your solution to check whether a graph has a cycle. The graph should be built from user input in the same way as in exercise 2. Your program should use the Graph ADT from the lecture (Graph.h and Graph.c). These files should not be changed.

      An example of the program executing is shown below for the following graph:


      ./cycleCheck
      Enter the number of vertices: 9
      Enter an edge (from): 0
      Enter an edge (to): 1
      Enter an edge (from): 1
      Enter an edge (to): 2
      Enter an edge (from): 4
      Enter an edge (to): 3
      Enter an edge (from): 6
      Enter an edge (to): 5
      Enter an edge (from): 6
      Enter an edge (to): 7
      Enter an edge (from): 5
      Enter an edge (to): 7
      Enter an edge (from): 5
      Enter an edge (to): 8
      Enter an edge (from): 7
      Enter an edge (to): 8
      Enter an edge (from): done
      Done.
      The graph is cyclic.
      

      If the graph has no cycle, then the output should be:

      ./cycleCheck
      Enter the number of vertices: 3
      Enter an edge (from): 0
      Enter an edge (to): 1
      Enter an edge (from): #
      Done.
      The graph is cycle-free.
      

      You may assume that a graph has a maximum of 1000 nodes.

      To test your program you can execute the dryrun program that corresponds to this exercise. It expects to find a program named cycleCheck.c in the current directory. You can use dryrun as follows:

      9024 dryrun cycleCheck
      

      Note: Please ensure that your output follows exactly the format shown above.

    [show answer]


  6. (Connected components)
    1. Computing connected components can be avoided by maintaining a vertex-indexed connected components array as part of the Graph representation structure:

      typedef struct GraphRep *Graph;
      
      struct GraphRep {
         ...
         int nC;  // # connected components
         int *cc; /* which component each vertex is contained in
                     i.e. array [0..nV-1] of 0..nC-1 */
         ...
      }
      

      Consider the following graph with multiple components:

      Assume a vertex-indexed connected components array cc[0..nV-1] as introduced above:

      nC   = 2
      cc[] = {0,0,0,1,1,1,0,0,0,1}
      

      Show how the cc[] array would change if

      1. edge d was removed
      2. edge b was removed

    2. Consider an adjacency matrix graph representation augmented by the two fields

      • nC   (number of connected components)
      • cc[]   (connected components array)

      These fields are initialised as follows:
      newGraph(V):
      |  Input  number of nodes V
      |  Output new empty graph
      |
      |  g.nV=V, g.nE=0, g.nC=V
      |  allocate memory for g.cc[]
      |  allocate memory for g.edges[][]
      |  for all i=0..V-1 do
      |     g.cc[i]=i
      |     for all j=0..V-1 do
      |        g.edges[i][j]=0
      |     end for
      |  end for
      |  return g
      

      Modify the pseudocode for edge insertion and edge removal from the lecture to maintain the two new fields.

      Hints:

      • Inserting an edge may reduce the number of connected components. If two components are merged, then the new component should adopt whichever ID is lesser of the two. Meanwhile the component with the largest ID should be re-assigned the greater of the two. This is to ensure the ID space remains contiguous from 0.

      • Removing an edge may increase the number of connected components. For such an edge (v, w), vertex v and all nodes reachable from it should be assigned the next available component ID, while w and all nodes reachable from it retain their original component ID.

    3. Download the Graph ADT from the lecture (Graph.h and Graph.c), rename them as GraphCC.h and GraphCC.c, and implement the above changes. You will also need to update the reference to Graph.h within GraphCC.c.

    4. Write a function in GraphCC.c that prints out the connected components array. You can see the expected output format in the sample output at the end of this exercise. The function signature is below, which you will also need to add as a prototype in GraphCC.h:

      void showComponents(Graph)
      
    5. A program connectedComponents.c is provided to drive your modified Graph ADT. It builds a graph from user input by:

      1. prompting the user for the number of vertices
      2. repeatedly asking the user to "i"" insert or "r"" remove an edge between two vertices, until multiple non-numeric characters are entered
      3. and then displays the connected components array.

      Copy the Makefile from Exercise 2 and modify it to compile GraphCC.c and connectedComponents.c. Its target should be an executable named connectedComponents.

      An example of the program executing is shown below for the graph in Exercise 2 where edges 1-2, 1-3, and 1-5 have subsequently been removed:

      ./connectedComponents
      Enter the number of vertices: 6
      [i]nsert or [r]emove an edge: i 0 1
      [i]nsert or [r]emove an edge: i 1 2
      [i]nsert or [r]emove an edge: i 4 2
      [i]nsert or [r]emove an edge: i 1 3
      [i]nsert or [r]emove an edge: i 3 4
      [i]nsert or [r]emove an edge: i 1 5
      [i]nsert or [r]emove an edge: i 5 3
      [i]nsert or [r]emove an edge: i 2 3
      [i]nsert or [r]emove an edge: r 1 2
      [i]nsert or [r]emove an edge: r 1 3
      [i]nsert or [r]emove an edge: r 1 5
      [i]nsert or [r]emove an edge: done
      Connected components:
      1, 1, 0, 0, 0, 0
      
      We have created a script that can automatically test your program. To run this test you can execute the dryrun program that corresponds to this exercise. It expects to find your modified
      • GraphCC.h
      • GraphCC.c
      • Makefile
      in the current working directory.

      9024 dryrun connectedComponents
      

      Note: Please ensure that your output follows exactly the format shown above.

    [show answer]


  7. (Hamiltonian/Euler paths and circuits)
    1. Identify any Hamiltonian/Euler paths/circuits in the following graphs:

    2. Find an Euler path and an Euler circuit (if they exist) in the following graph:

    [show answer]


  8. Challenge Exercise

    Write pseudocode to compute the largest size of a clique in a graph. For example, if the input happens to be the complete graph K5 but with any one edge missing, then the output should be 4. Extend your program from Exercise 2 by an implementation of your algorithm to compute the largest size of a clique in a graph.

    Hint: Computing the maximum size of a clique in a graph is known to be an NP-hard problem. Try a generate-and-test strategy.

    [show answer]


Assessment

After you've solved the exercises, go to COMP9024 23T3 Quiz Week 4 to answer 5 quiz questions on this week's problem set and lecture.

The quiz is worth 2 marks.

There is no time limit on the quiz once you have started it, but the deadline for submitting your quiz answers is Tuesday, 10 October 5:00:00pm.

Please continue to respect the quiz rules:

Do …

Do not …

Reproducing, publishing, posting, distributing or translating this page is an infringement of copyright and will be referred to UNSW Conduct and Integrity for action.