Graph Representations
Context: FIT1058_MOC · symbolic ways to store a Graph in memory · edge list, adjacency matrix, adjacency list, incidence matrix · FIT2004_MOC · the W5 ADT selection decision that sets every traversal bound (LO3)
Quick Revision
- 🎯 Objective: store a Graph symbolically for algorithms ➔ four standard encodings, two used in practice.
- 📦 Core Components: edge list ➔ adjacency matrix (, lookup) ➔ adjacency list (, output-sensitive scan) ➔ incidence matrix.
- ⚡ Key Constraint: the representation, not the algorithm, fixes the bound — DFS is on a list and on a matrix, from identical code.
📝 Core
1. The Four Representations
- Edge list ➔ set of edges (+ , else isolated vertices vanish).
- Adjacency matrix ➔ array; unweighted stores
True/False, weighted stores the weight; undirected ⟹ symmetric, zero diagonal. - Adjacency list ➔ an array of vertex objects, each holding a list of the edges leaving it (with weights).
- Incidence matrix ➔ bit array, iff vertex is an endpoint; each column has two s — used mainly for proofs, where row sums give (Degree and the Handshaking Lemma) and the total s give .
2. FIT2004 Costs (W5)
- Matrix, space ➔ unconditionally — allocated whether or not the edges exist, so a sparse graph pays the full square.
- Matrix, time ➔ to test whether edge exists · to walk all neighbours of , scanning a whole row including empty cells.
- List, space ➔ — vertex slots plus edge records across all lists.
- List, time ➔ to retrieve all neighbours of , that vertex’s neighbour count — output-sensitive (Output-Sensitive Complexity), and summed over all vertices it is , which is where traversal’s comes from.
- List, edge lookup ➔ nominally if each list is kept sorted, but you cannot binary-search a linked list; the real cost stays with an early exit once a larger vertex id is reached.
- The selection rule ➔ sparse () ⟹ adjacency list; dense () ⟹ the matrix costs the same space and buys lookup. Real graphs (road networks, the web, social graphs) are sparse, so the list is the default.
3. Reading Less Than the Matrix — the Universal Sink in (applied P13, advanced)
- The object ➔ a universal sink is a vertex with in-degree and out-degree : every other vertex points at , and points at nobody.
- Its matrix signature ➔ row is all zeros (no out-edges) and column is all ones except the diagonal (every in-edge present).
- At most one exists ➔ two sinks would each need an edge into the other, contradicting out-degree — so the task is to find the candidate, not a set.
- The required bound forbids reading the input ➔ on a matrix means at most of its cells may be examined, so every cell read must eliminate a candidate permanently.
- The two deductions from one cell ➔ ⟹ has an out-edge ⟹ is not a sink · with ⟹ is missing an in-edge ⟹ is not a sink. Either way exactly one vertex dies per probe.
- Why a naive column scan is still ➔ eliminating one vertex per cell is not enough if you re-scan a full column of cells per candidate; the elimination must also stop you revisiting the dead.
- The linked-list mechanism ➔ hold all live candidates in a linked list. To test a column, scan only the rows still in the list (skipping the diagonal). Every element scanned eliminates one vertex, which is spliced out of the list and never considered again ⟹ total scanning across the whole run is .
- The finish ➔ one candidate remains; verify it directly by checking its whole row is and its whole column is off-diagonal — cells, and the answer may still be “no universal sink”.
- The transferable move (LO1) ➔ when the demanded bound is below the input size, look for a constant-cost step that discards a candidate per unit read — the same shape as Cycle Detection’s undirected argument, where the work before an early halt is what you bound.
⚖️ Core Decision Matrix
| Representation | Space | Edge-exists test | All neighbours of | Best for |
|---|---|---|---|---|
| adjacency matrix | dense; frequent “is there an edge?” queries | |||
| adjacency list | (early exit if sorted) | sparse; traversal-dominated workloads | ||
| edge list | scan | compact storage, Bellman-Ford-style edge sweeps | ||
| incidence matrix | — | — | proofs |
When It Flips: the adjacency matrix is the Binary Relation's matrix over . The crossover is density: below the list wins on space and on neighbour iteration, and only an edge-existence requirement buys the matrix back — and only when cells are affordable at all.
📊 Exam Execution Trace
Manual Execution Trace
Rows for (order –):
| Step / State | Vertex | Row | Degree |
|---|---|---|---|
| 0 (Init) | — | — | — |
| 1 | 2 | ||
| 2 | 3 | ||
| 3 | 0 |
Applied Exercise
Problem: run BFS over , under both representations.
Final Extracted Output: a factor of in time and worse in space, from identical traversal code — the bound belongs to the representation. Quote it as ” on an adjacency list”; an unqualified is an incomplete answer.
⚠️ Common Mistakes
- 💡 Edge list needs ➔ isolated vertices have no edges; the matrix is cells regardless, the list stores only actual edges.
- 💡 Claiming edge lookup on an adjacency list ➔ only if the neighbours sit in a sorted array; a linked list has no random access, so it degrades to a scan with early termination.
- 💡 Reading the whole matrix when the bound is ➔ cells exist, so a sub-quadratic bound is a promise not to read them; find the elimination step first, then the data structure that stops you re-reading.
- 💡 Adding the adjacency list to a traversal’s auxiliary space ➔ the graph is input, not auxiliary (Algorithmic Complexity); auxiliary is the queue and flags.
🧠 Active Recall
Describe the adjacency matrix and adjacency list, and when each is preferable.
- Hint: Dense vs sparse.
Answer
- Short answer: matrix cells, test, space; list neighbours per vertex, space, neighbour scan.
- Why: Sparse wins list ➔ real graphs store no empty entries, and traversal only ever asks for all neighbours, which the list answers output-sensitively.
An algorithm asks "does edge exist?" times on a graph with . Which representation, and what changed?
- Hint: The workload, not the graph, moved.
Answer
- Short answer: the matrix — cells is affordable, and lookups beat list scans.
- Why: Representation is chosen against the operation mix, not the graph alone ➔ traversal wants “all neighbours” and favours the list; membership testing wants random access and favours the matrix. The space penalty must first be survivable ➔ at the same argument fails because cells cannot be allocated. This is LO3 in miniature ➔ every “X vs Y” answer must end in a selection rule, not a feature list.
Why does the universal-sink problem need a linked list at all, when each matrix cell already eliminates a vertex?
Answer
- Short answer: eliminating one vertex per cell still allows cells per candidate; the list guarantees each elimination is charged to a cell that is never scanned again.
- Why: The deduction is already optimal per cell ➔ kills , with kills , so no probe is wasted. The waste is in re-scanning ➔ testing a column top-to-bottom re-reads rows belonging to vertices already eliminated, giving per column and overall. Splicing makes the total telescope ➔ scanning only live candidates means every cell read removes an element from the list permanently, so at most cells are read across the entire elimination phase, plus to verify the survivor.