Adjacency list Created 2026-10-13 Updated 2026-10-20
An adjacency list represents a graph by keeping, for each vertex , a collection of the vertices reached by its outgoing edges. An array indexed by vertex identifiers can locate each collection; the collection itself may use a linked list or an array. In an undirected graph, each nonloop edge supplies an entry at both endpoints.[ref]
Graph.java. Graph.java, class documentation and fields: vertex-indexed adjacency collections; addEdge: entries at both endpoints
For a worked example, take the simple graphwhere denotes the unordered pair . Its adjacency lists, displayed in label order, areThus has three neighbours, although the graph has six vertices. Its list contains exactly the destinations available for a single step from ; no slots are reserved there for . The fourteen neighbour entries represent seven edges, with appearing as in 's list and as in 's list. The label order makes the example easy to read; an adjacency list need not be sorted.
Seven undirected edges and their fourteen adjacency list entries
. Details. The useful idea is to store existing connections explicitly. Neighbour enumeration can then follow exactly the possible next steps, which is what breadth-first search and depth-first search need. In a sparse graph, most pairs of vertices have no edge; adjacency lists avoid reserving a separate adjacency matrix entry for every such absence. Whether this saves work depends on the question: neighbour enumeration and testing one proposed edge have different costs.
Adjacency list edge conventions 2026-10-14
For a directed graph, store in for the edge . The reverse edge is separate and is stored only if it exists. Consequently, list length is outdegree, not indegree, and each directed edge contributes one entry in total. An incoming-list index is additional storage.[ref]
Digraph.java. Digraph.java, addEdge, adj and outdegree: one entry in the tail's collection; indegree is maintained separately
Orient the seven edges of our example from earlier to later labels. The resulting directed graph hasThere are now seven entries, with outdegrees . In particular, needs an empty list even though it is reached from : incoming edges do not make 's outgoing list nonempty. Replacing each directed edge by an undirected graph connection would lose this distinction.
Our running simple graph excludes self-loops and repeated edges. If those restrictions are relaxed, specify how the adjacency list encodes them. Here an undirected self-loop at contributes two copies of to , matching its contribution of two to the degree of a vertex. A directed self-loop contributes one copy, and one each to outdegree and indegree. Distinct parallel edges retain distinct entries; a multigraph's list length can therefore exceed its number of distinct neighbours.[ref]
Graph.java. Graph.java, class documentation: parallel edges and two-entry undirected self-loop convention; addEdge applies both insertions even when v equals w
[ref]Digraph.java. Digraph.java, class documentation and addEdge: parallel edges permitted; a directed self-loop adds one outgoing entry and increments indegree once
For example, add one undirected self-loop at to the seven-edge example. Its list becomes , the vertex degree of becomes four, and the total entry count becomes sixteen for eight edges. If an application instead stores a self-loop once, it must account for its doubled undirected vertex degree separately. In a weighted graph, an entry can be an ordered pair meaning destination and weight ; the weight belongs to the edge, not to the destination vertex.
Adjacency list storage and query costs 2026-10-14
Write and . Suppose each vertex identifier, list descriptor, link and optional edge weight occupies a constant number of machine words, and indexed array access and identifier comparison take constant time. There are list descriptors and directed, or undirected, neighbour entries. Hence adjacency lists use machine words in Big O notation. A full adjacency matrix has entries, regardless of . These are counts under a storage model, not fixed byte sizes.[ref]
Recitation 19: Graph Representations and Traversals (Cornell course page). Recitation 19, The Adjacency List and The Adjacency Matrix: directed and undirected entry totals, linear list storage, and quadratic full-matrix storage
Our undirected example has six list descriptors and fourteen neighbour entries, versus thirty-six full adjacency matrix entries. The numbers and count different kinds of logical items: they do not prove a particular byte saving. A linked list also needs links; an array may have unused capacity. Storing only half of a symmetric matrix changes its exact count while leaving quadratic growth. If identifiers or weights require growing numbers of machine words, their size must be included in the space complexity.
An unsorted adjacency list answers "does the edge exist?" by linear search through . In the worst case it inspects all entries, giving time, or for . Even if is absent, an uninspected entry could still contain it, so arbitrary lists require the whole scan. On our example, looking for in 's list compares with and fails after three comparisons. A full adjacency matrix instead answers from its single entry in time.[ref]
Recitation 19: Graph Representations and Traversals (Cornell course page). Recitation 19, The Adjacency List: edge-membership question; The Adjacency Matrix: constant-time edge-existence query
Different promises change the query. A sorted array of neighbour identifiers permits binary search in time, but maintaining order after insertion may require shifting entries. A per-vertex hash table permits expected constant-time membership under the hashing and load assumptions of that data structure; it does not give that guarantee to an arbitrary adjacency list. A stored list length can likewise make a vertex degree query constant-time even though neighbour enumeration still has to visit its entries.
The advantage grows with scale. For a graph path on vertices, , so there are undirected neighbour entries and descriptors, versus full adjacency matrix entries. Such a family is a sparse graph with . For a complete graph, , so the undirected neighbour entries already number : the adjacency list advantage in growth rate disappears. Choose by the workload: following existing edges favours adjacency lists; many arbitrary pair-membership queries can favour a full adjacency matrix. The representation organizes the same graph for different questions.
Bipartite graph Created 2026-09-24 Updated 2026-10-14
A bipartite graph separates its vertices into two independent sets: every edge goes from one part to the other. Here is a finite simple graph and an undirected graph, with no self-loops and no repeated edges. The two parts satisfyThus neither part contains an edge. The parts need not have equal sizes, and some pairs across them may have no edge; requiring every cross pair gives a complete bipartite graph. The independent set and cross-edge definitions are equivalent.[ref]
The Basics (author-hosted chapter preview). Section 1.1, printed p. 3, independent vertices; Section 1.6, printed p. 17, bipartite and complete multipartite graphs
For example, the six-vertex cycle graph with edges has parts and . Drawing the same graph as a hexagon or in two columns changes neither its edges nor its bipartite property. An extra isolated vertex can go in either part.
We allow either part to be empty. Consequently a graph with no edges is bipartite, even when ; choose , . Here "two colours" means two colours are available, with no requirement to use both. This convention explicitly includes the empty case, whereas definitions requiring two nonempty classes need an exception. For several connected components of a graph, choose the two parts separately in each graph component and take their unions. In a nonempty connected graph that is bipartite, fixing one vertex's colour forces all others along graph paths, so its two-colour assignment is unique up to swapping the colours. Each graph component, including an isolated vertex, can be swapped independently.
The two-colourability criterion for bipartite graphs identifies these parts with the colour classes of a proper vertex colouring: assign colour to and colour to , so adjacent vertices have different colours. More surprisingly, the odd-cycle characterization of bipartite graphs turns a global requirement into one precise obstruction:This statement is for the finite undirected graph and simple graph model above; a graph cycle has distinct vertices except for the repeated starting vertex. Along a graph cycle in a bipartite graph, colours alternate, so returning to the starting colour takes an even number of edges. Therefore any odd cycle, whether or not other edges join its vertices, prevents a bipartite split.[ref]
The Basics (author-hosted chapter preview). Section 1.6, Proposition 1.6.1 and proof, printed pp. 17–18
Breadth-first search supplies the converse constructively. Start an uncoloured vertex with colour , and use a FIFO queue to explore its graph component. When an edge first discovers , give the opposite colour to , record as its parent in a rooted tree, and enqueue . If both endpoints of any scanned edge already have the same colour, stop with a conflict. Restart from an uncoloured vertex until every graph component has been examined. If no conflict occurs, all edges cross the two colour classes, which are the required independent sets.[ref]
A conflict also explains the failure. In the rooted tree produced by breadth-first search, let be the graph distance from . The colour of is its distance's integer parity. An edge in an undirected graph satisfies , so equal colours force . Follow the two parent chains upward to their lowest common ancestor , discard the shared graph path from to , and join the remaining two graph paths with the conflict edge . The result is a genuine graph cycle withedges, an odd integer. The two branches have no shared vertices except , so this is more than a closed walk. This odd cycle is a certificate that anyone can check directly, using only its listed vertices and edges.
In the example, breadth-first search from gives , , and . The conflict edge closes the odd cycle , with five edges. The common edge is excluded. Thus the same procedure returns either two independent sets covering , or an explicit odd cycle. With adjacency lists and constant-time FIFO queue operations, its time complexity is in Big O notation, with additional storage.[ref]
The two parts often have a practical meaning: workers and jobs, with an edge exactly when a worker can perform a job. A matching in a graph chooses compatible worker–job pairs with no reused endpoint; merely being bipartite does not guarantee enough compatible pairs. Hall's marriage theorem characterizes when every worker can be matched, and the assignment problem adds costs to seek an optimal assignment. These connections make the bipartite structure useful after recognition, as the starting point for selecting edges under constraints.
Breadth-first search Created 2026-09-24 Updated 2026-10-14
Breadth-first search is a graph search that explores successive layers of graph distance from a starting vertex . Here the graph is an undirected graph on a finite set of vertices, and distance counts edges, with no differing edge weights. Mark as discovered, assign , and place it in a FIFO queue. Repeatedly remove the front vertex and scan its adjacency list. Whenever a graph neighbour is still undiscovered, mark it immediately, record as its parent in a rooted tree in the discovery tree, assign , and enqueue at the rear. Marking on insertion ensures that another edge cannot enqueue the same vertex again. [ref]
https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/BreadthFirstPaths.java. Sedgewick and Wayne, Algorithms: BreadthFirstPaths queue and discovery rule
Use the same undirected graph as depth-first search, with vertex set and edgeswhere denotes the unordered pair . Start at and scan graph neighbours in lexicographic order of their one-letter labels. The diagram's generator computes the following layers and the parent in a rooted tree assigned to each discovered vertex; the source is the root of a tree and has no parent in a rooted tree.
| Graph distance from | Vertices in discovery order | Parent in a rooted tree in the same order |
|---|---|---|
| none | ||
The discovery and removal orders both happen to be . After processing , the FIFO queue is : must be processed before the newer . Processing then skips the already discovered and enqueues , giving . Processing adds nothing. Processing discovers , giving ; later sees already marked and leaves its parent in a rooted tree unchanged. The discovery edges are , forming a rooted tree on all seven vertices even though the original graph contains graph cycles. The discovery tree contains six edges; it need not contain every examined edge.
First arrival is shortest because the FIFO queue keeps assigned distances in nondecreasing order, with entries from at most two consecutive layers. While processing distance , newly discovered vertices receive distance and join the rear. If an undiscovered vertex had a shorter graph path, its preceding vertex on that graph path would lie in an earlier layer and would already have discovered it. Thus is the minimum number of edges from to . This is a mathematical induction over the layers, not an assumption about the drawing. Following the parent in a rooted tree backwards at each step gives ; reverse that sequence to obtain the three-edge graph path . is another shortest graph path, but the specified graph neighbour order makes discover first. Changing that order can change the parent in a rooted tree without changing graph distances. [ref]
https://ocw.mit.edu/courses/6-006-introduction-to-algorithms-fall-2011/1208e162775f6f5cedfbb9f2b694ede0_MIT6_006F11_lec13.pdf. MIT 6.006, lecture 13: distance layers and shortest paths, pp. 4–6
The shortest-route guarantee concerns the shortest path problem measured by edge count. It also minimizes total cost when every edge has the same positive cost. With unequal edge weights, first discovery need not minimize total cost; nonnegative costs can instead be handled by the Dijkstra algorithm. Breadth-first search is useful for minimum-hop routes and for finding all vertices within a specified graph distance. Its FIFO queue finishes an older layer before the next one, whereas depth-first search uses a call stack or explicit LIFO stack to pursue one branch immediately.
With adjacency lists and constant-cost discovery checks, together with constant worst-case or amortized analysis cost for FIFO queue insertion and removal, the time complexity is in Big O notation. Every reached vertex is enqueued and removed once, each incident edge is scanned at most twice in an undirected graph, and initializing all marks and distances takes . The FIFO queue contains at most vertices; the example's maximum is three. Discovery marks, distances and the recorded parent in a rooted tree for each nonroot vertex also give auxiliary space complexity, excluding the graph. An array that shifts every entry on front removal can lose the linear time complexity; the FIFO queue abstraction alone does not make removal constant-cost. Ordering the adjacency lists is a separate preprocessing cost.
A single start reaches only its connected component of a graph. Unreached vertices retain distance and have no recorded parent in a rooted tree. Restarting at each still-undiscovered vertex, while preserving all discovery marks, produces a forest covering the graph; each new root of a tree gets distance zero, so these distances refer to that root of a tree, not to the original .
Cached cell value 2026-10-13
A numerical value retained for reuse by memoization. It is usable only while the spreadsheet formula and all its relevant inputs remain valid. After changing
A1 from 3 to 5, following outgoing directed edges finds exactly B1,B2,C1,D1. Invalidate their cached cell values before evaluating another requested result; install the new input value for A1. The cached cell values of A2 and E1 remain valid because neither has a directed path from A1.Downstream invalidation after an input edit, and a different formula edit creating a spreadsheet circular reference
. Details. One recomputation in the restricted topological order
B1,B2,C1,D1 gives . Four spreadsheet formulas are recomputed, while E1 stays at 16. Removing invalid values from the cache prevents a subtle error: a finished marker from the previous evaluation does not prove that the old value is correct after an edit. The affected set is conservative; a changed prerequisite might leave a consumer's value numerically equal, but this simple scheme still invalidates it. For a formula edit, update the incoming directed edges and corresponding outgoing consumer lists as well as invalidating the edited spreadsheet cell and its downstream consumers. Microsoft's documentation explicitly describes both direct and indirect dependents being marked for recalculation.[ref]Excel Recalculation by Microsoft. Dependence, Dirty Cells, and Recalculated Cells: paragraph beginning “When a structural change is made”; B1/A1 and C1/B1 dirty-cell example
For demand-driven evaluation, memoization combines with depth-first search. Each spreadsheet cell has one of three states:
UNSEEN means no valid cached cell value; ACTIVE means its evaluation is on the current call stack; FINISHED means a valid value is available. Initialize valid input spreadsheet cells as FINISHED. Start with all spreadsheet formulas UNSEEN, or retain only valid FINISHED entries after invalidation. The following original algorithm uses an ordered references list and an apply_formula operation for each spreadsheet formula:def value(cell):
if state[cell] == FINISHED:
return cache[cell]
if state[cell] == ACTIVE:
i = stack.index(cell)
raise CircularReference(stack[i:] + [cell])
state[cell] = ACTIVE
stack.append(cell)
try:
args = [value(p) for p in references[cell]]
cache[cell] = apply_formula(cell, args)
state[cell] = FINISHED
return cache[cell]
except Exception:
state[cell] = UNSEEN
raise
finally:
stack.pop()Requesting
D1 first makes D1,C1,B1 active, then reads the finished inputs A1,A2. B1 finishes at 7. Evaluating B2 reuses the finished value of A1 and finishes at 6; C1 finishes at 13 and D1 at . A later request for D1 immediately returns its cached cell value. The shared prerequisite A1 is not a directed cycle: it was finished before the second request. The crucial distinction is between a previously visited vertex and a still-active vertex. The exceptional path restores unfinished states while unwinding the call stack, so failed evaluations are not cached as successful results. MIT's depth-first search notes use stack-duration marks to distinguish an edge to an active ancestor and to detect a directed cycle.[ref]Lecture 14: Graphs II: Depth-First Search. Lecture 14: Graphs II: Depth-First Search, printed pp. 3–4: stack-duration marks and cycle detection
With adjacency lists and constant-time state/cache access, the structural work of a successful demand-driven evaluation is in big O notation, where and are the requested spreadsheet cells and their recursively reached prerequisites and references. Each reached spreadsheet formula is computed once; each reference is inspected once. This time complexity counts dependency bookkeeping separately from the cost of the arithmetic operations. Detecting a spreadsheet circular reference can additionally scan the current call stack once to report its witness.
Call stack Created 2026-10-13 Updated 2026-10-24
A call stack stores active procedure calls and the information needed to resume each caller, such as its return location and local variables. A call adds a frame; returning removes that frame and resumes the previous call. When depth-first search uses recursion, a frame also records where its adjacency list scan paused. The active frames describe a graph path from the starting vertex to the current vertex; finished calls have left the call stack, although their vertices remain marked as discovered.
Conditional expectations algorithm cost 2026-10-18
Directly scanning all edges to evaluate each child of each of choices takes elementary operations, in Big O notation. There is a faster implementation. When assigning vertex , let and count its already assigned graph neighbours labelled zero and one. Contributions from edges not incident to are unchanged, and contributions from edges to unassigned graph neighbours stay for either choice. ThereforeChoose zero if , otherwise one: put on the side with fewer already assigned graph neighbours. With adjacency lists, scanning each vertex's list once costs operations overall and uses storage including the graph. These bounds count operations on labels and counters that fit in machine words of bits. Multiplying the conditional expectation cut formula by two also permits exact integer comparisons without rounding.
If is the size of a maximum cut, then . The constructed cut of a graph thus has size at least , an approximation ratio of in the maximization convention. The five-versus-six example illustrates the gap between this guarantee and exact optimization.
For a general randomized algorithm, the averaging proof alone does not establish efficient derandomization. One needs a polynomial number of commitments, an efficient way to find a suitable child at each step, and manageable representations and evaluation costs for the relevant conditional expectations. Those evaluations can themselves be hard. Enumerating all completions verifies this small example, but would take exponentially many assignments as grows. The polynomial-time algorithm here comes from the local conditional expectation cut formula, not from that enumeration.
Cut vertex Created 2026-10-06 Updated 2026-10-14
A cut vertex, also called an articulation point, is a vertex of an undirected graph whose deletion increases the number of connected components of a graph. Write for that count, including isolated vertices, and for the graph obtained by removing and every incident edge. Then the test is , even when the original graph is disconnected. By contrast, deleting a bridge in a graph removes just one edge and keeps both endpoint vertices. These are different failure models. [ref]
https://algs4.cs.princeton.edu/41graph/. Sedgewick and Wayne, Algorithms, §4.1: bridges and articulation points
First take two triangles in a graph, and , sharing only the vertex . This simple graph hasHere abbreviates the unordered pair . Removing leaves the two connected components of a graph and , so . Removing any other vertex leaves a connected graph. Thus is the only cut vertex. Every edge lies on a triangle in a graph, so deleting any single edge still leaves a graph path between its endpoints along the other two edges. There are no bridges in a graph.
The generator computes the adjacency lists and checks every vertex deletion, giving the following complete table:
| Deleted vertex | Original graph neighbours of | |
|---|---|---|
Now take two disjoint triangles in a graph, and , joined by the edge :Deleting only preserves all six vertices and leaves connected components of a graph and : is the only bridge in a graph. Deleting also removes and , leaving connected components of a graph and . Deleting instead leaves and . Thus are exactly the cut vertices.
Again the adjacency lists and all vertex deletion counts are computed directly:
| Deleted vertex | Original graph neighbours of | |
|---|---|---|
For a disconnected example, adjoin an isolated vertex to the first graph. Now , deleting gives , and deleting gives . The original graph was already disconnected, but is still a cut vertex; is not. The empty graph has count zero, so deleting the only vertex of a one-vertex graph does not create a cut vertex. Nor is either endpoint of a single isolated edge a cut vertex, although that edge is a bridge in a graph.
In a communications network, a cut vertex models a router whose failure separates surviving routers. The shared- example shows why having an alternative graph path around each individual edge does not protect against failure of the common router. An additional edge from to removes this particular vulnerability: after deleting , still join the two sides. A bridge in a graph instead models a vulnerable link. This distinction tells us whether to add another cable between existing devices or a route avoiding an indispensable device; it describes connectivity, rather than traffic capacity or failure probabilities.
The beautiful structural consequence is that a graph splits into biconnected components joined at cut vertices. Within each nontrivial robust piece, a single vertex failure cannot separate the other vertices; between pieces, the shared cut vertices reveal the bottlenecks. Also, a Hamiltonian graph has no cut vertex: deleting any vertex from its Hamilton cycle leaves a Hamiltonian path through all remaining vertices.
Historically, Tarjan's June 1972 paper developed depth-first search as a tool for finding biconnected components in time linear in the numbers of vertices and edges. [ref]
https://epubs.siam.org/doi/10.1137/0201010. Robert Tarjan, Depth-First Search and Linear Graph Algorithms, SIAM Journal on Computing 1(2), 146–160 (1972)
In his 1983 recollection, Tarjan credited Hopcroft with the original linear-time biconnected component algorithm during their collaboration beginning in summer 1970, and described the published version as a refinement. The historical lesson is how a carefully organized traversal turns many separate deletion experiments into one computation. [ref]https://garfield.library.upenn.edu/classics1983/A1983QY47200001.pdf. Robert E. Tarjan, Citation Classic recollection (1983)
DAG shortest-path algorithm 2026-10-31
Initialize source distance to zero and other distances to infinity, then process vertices in topological order, relaxing their outgoing edges once. Every predecessor on a path to a vertex appears earlier, so induction through that ordering proves that its distance is final when processed. Negative weights are allowed because there are no directed cycles. With adjacency lists, topological sorting and all edge relaxations take time; predecessor pointers reconstruct paths.
Depth-first search Created 2026-10-13 Updated 2026-10-20
Depth-first search is a graph search that follows a branch as far as it can before returning to try the next unexplored branch. Starting at a vertex of an undirected graph on a finite set of vertices, mark as discovered. Scan its graph neighbours in a fixed order: for each undiscovered vertex, record the current vertex as its parent in a rooted tree in the discovery tree, mark the new vertex, and explore it immediately using recursion. Return when its adjacency list is exhausted. Discovery marks persist after return, preventing a graph cycle from causing repeated exploration. This branch-and-return rule is the maze exploration described in MIT's notes. [ref]
Use the same unweighted undirected graph as breadth-first search, with vertex set and edgeswhere denotes the unordered pair . Visit graph neighbours in lexicographic order of their one-letter labels, starting at . The discovery order, computed by the diagram's generator, isThe discovery edges in order are . They form a rooted tree spanning the reached vertices, with root of a tree . The other edges, and , remain in the original graph but never discover a new vertex. In particular, the graph cycle causes no repeated discovery.
At there is no undiscovered graph neighbour, so its call returns to . The next call enters , then , then , then . Immediately after discovering , the call stack is , with on top; is still discovered but its call has finished. The maximum call stack depth is six, although seven vertices are discovered. Following the recorded parent in a rooted tree backwards at each step reconstructs a graph path; here the route from to is , using five edges. Breadth-first search instead finds , using three edges. Depth-first search establishes reachability but does not guarantee the fewest edges.
An explicit LIFO stack can replace recursion, but its entries and discovery rule must be specified. To reproduce this exact traversal, store one frame per active vertex, retaining the next position in its adjacency list. Advance the top frame by one graph neighbour; if that vertex is undiscovered, mark it and push its frame immediately; pop an exhausted frame. Pushing all undiscovered graph neighbours at once and marking them on push is a different iterative variant. Even pushing them in reverse lexicographic order does not generally reproduce the parent in a rooted tree relationships above: would already be marked while processing , so could not discover .
With an adjacency list representation, constant-cost discovery checks and frame operations, and the graph neighbours already ordered, depth-first search has time complexity in Big O notation. Each reached vertex is entered once and each incident edge is examined at most twice in an undirected graph; initializing all discovery marks costs . The call stack has depth at most , since no active vertex repeats. Discovery marks and the recorded parent in a rooted tree for each nonroot vertex also need auxiliary space complexity, excluding storage for the graph. Sorting adjacency lists to obtain a chosen order is additional work. [ref]
https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/DepthFirstPaths.java. Sedgewick and Wayne, Algorithms: DepthFirstPaths discovery marks and resource bounds
One start explores only its connected component of a graph. To cover a disconnected graph, start again at every still-undiscovered vertex while keeping all discovery marks; the discovery trees form a forest. Depth-first search is useful for reachability, constructing a spanning tree of a connected graph, and organizing the return steps needed by a depth-first traversal of a tree. For detecting a graph cycle in a simple graph that is an undirected graph, an edge to an already discovered vertex other than the current parent in a rooted tree closes a graph cycle; the immediate return edge to that parent in a rooted tree is not itself a graph cycle.
// Target: computer-science.bigb
DFS lowpoint 2026-10-14
For a finite simple graph that is an undirected graph, the DFS lowpoint of is the smallest DFS discovery time reachable by following zero or more discovery edges down its rooted tree, then at most one non-discovery edge to an ancestor. Staying at is allowed. Ignore the reverse traversal of the discovery edge to the parent in a rooted tree: it is the same edge, not an escape route. Start with . On returning from a child in a rooted tree , replace by . For a non-discovery edge from to an earlier ancestor , replace it by . This summarizes how far upward the explored branch can reconnect. [ref]
https://sites.cs.ucsb.edu/~gilbert/cs240a/notes/TarjanDFS.pdf. Tarjan (1972), §3, LOWPT recurrence, pp. 151–153
The cut vertex criterion has a separate case for each root of a tree in the discovery forest:
- A nonroot vertex is a cut vertex exactly when some child in a rooted tree satisfies .
- A root of a tree is a cut vertex exactly when it has at least two children in a rooted tree, counting discovery edges, rather than all graph neighbours.
- A discovery edge is a bridge in a graph exactly when . A non-discovery edge is never a bridge in a graph.
Why does equality matter? If , the branch below can reconnect to , so the discovery edge lies on a graph cycle and is not a bridge in a graph. Deleting the vertex destroys that reconnection too. If has a parent in a rooted tree, the branch is then separated from that parent in a rooted tree, explaining the non-strict cut vertex test. For a root of a tree, there is no earlier side to separate from; distinct discovery branches cannot have an edge between them, since depth-first search would have explored them as one branch. This gives the two-children in a rooted tree rule. These are proof outlines of the criteria. [ref]
https://algs4.cs.princeton.edu/41graph/Biconnected.java.html. Sedgewick and Wayne, Biconnected.java: discovery, lowpoint and root tests
[ref]In the joined- example with DFS discovery times , the DFS lowpoints in label order are . For and its child in a rooted tree , : is a bridge in a graph, and is a cut vertex. For and its child in a rooted tree , : is a cut vertex, although is not a bridge in a graph. The root of a tree has only one child in a rooted tree, , so it is not a cut vertex. In the shared- example, the root of a tree has two children in a rooted tree, and , and is a cut vertex, while every DFS lowpoint is and no edge passes the strict bridge in a graph test.
Restart depth-first search at every undiscovered vertex to cover a disconnected graph, applying the root of a tree rule separately in each connected component of a graph. With adjacency lists and constant-cost label operations, computing all DFS lowpoints, cut vertices and bridges in a graph has time complexity in Big O notation, because each vertex is discovered once and each edge is inspected twice. The simple parent-skipping rule assumes a simple graph. In a multigraph, skip only the actual parent edge, identified individually: a parallel edge to the same parent in a rooted tree is a valid reconnection and can prevent the first edge from being a bridge in a graph. The criteria here concern undirected graphs; a directed graph requires a different connectivity analysis.
Dijkstra algorithm 2026-10-14
The Dijkstra algorithm solves the single-source shortest path problem in a finite graph with nonnegative edge weights, finding distances to all reachable vertices. Start with tentative distance and elsewhere. Repeatedly choose the unfinalized vertex with least tentative distance, declare that label permanent, and perform edge relaxation on its outgoing edges. For an undirected graph, inspect both directions. A finite tentative distance always records the cost of a discovered graph path, but can improve before finalization.
In our example, finalizing discovers at , at , and at . Choose next: edge relaxation through lowers to and to . Choose : edge relaxation through lowers to . Finally choose . The permanent distances are therefore , , , ; the evolving tentative distances were and .
Tentative distances fall until the Dijkstra algorithm makes each label permanent
. Details. Store a shortest-path predecessor whenever edge relaxation improves a label. The final table contains enough information to reconstruct routes, rather than merely their costs:
| Vertex | Distance from | Shortest-path predecessor | Finalization order |
|---|---|---|---|
| None: source | |||
Following shortest-path predecessors backwards gives ; reversing the order gives . If only this destination matters, stop when is finalized. Stopping when is first discovered would incorrectly return . If the smallest remaining tentative distance is , all remaining vertices are unreachable.
Why is the permanent choice safe? Suppose the selected vertex had a cheaper graph path. Let be the first unfinalized vertex on that graph path. Its preceding vertex was finalized, so edge relaxation has already offered a label no larger than that prefix's cost. Nonnegative remaining edge weights make the whole cost at least this prefix's cost; selection gives . A cheaper graph path would contradict these comparisons. Zero weights are allowed; ties can be resolved arbitrarily. This is the reason the label-setting shortest-path algorithm works, rather than a lucky feature of the drawing.
Negative weights break that reasoning. In a directed graph with costing , costing , and costing , the Dijkstra algorithm would finalize at before discovering the cheaper cost . The Bellman-Ford algorithm handles negative edge weights when no relevant negative directed cycle exists.
With an adjacency list and an indexed binary heap implementing a priority queue, the time complexity is for , using Big O notation. Each vertex is extracted at most once, each edge is inspected once per direction, and each successful edge relaxation decreases a heap key in time. This assumes constant-cost arithmetic and comparisons; exact computation with very large weights adds their representation cost. The queue uses space beyond the graph. Simply scanning all remaining vertices gives instead.[ref]
https://algs4.cs.princeton.edu/44sp/DijkstraSP.java.html. Princeton, DijkstraSP implementation
[ref]https://algs4.cs.princeton.edu/code/javadoc/edu/princeton/cs/algs4/IndexMinPQ.html. Princeton, indexed binary heap operations
The 1959 paper presents the shortest path problem as its second problem, after the minimum spanning tree problem, and grows known shortest routes in increasing distance order.[ref]
https://www.cs.yale.edu/homes/lans/readings/routing/dijkstra-routing-1959.pdf. Dijkstra, A note on two problems in connexion with graphs, pp. 269–271
In his later recollection, Dijkstra dated the shortest-route discovery to 1956: a demonstration for the ARMAC computer used a simplified Dutch railway map and printed the route between towns. A practical request became an enduring algorithm because the same distance rule applies to any suitable graph.[ref]https://garfield.library.upenn.edu/classics1983/A1983QA19900001.pdf. Dijkstra's recollection, published in 1983
Graph transpose 2026-10-25
The directed graph obtained by reversing every edge while retaining the same vertices. Scanning adjacency lists constructs the graph transpose in time. A path reverses direction, while mutual reachability and therefore strongly connected components are unchanged.
Hierholzer's algorithm 2026-10-14
Hierholzer's algorithm constructs an Euler circuit by growing closed trails along unused edges and splicing them at shared vertices, as in the Euler circuit criterion. Each edge must have a distinct record. An implementation using adjacency lists, a stack, and a used flag for each edge runs in time, including the connectivity and vertex degree checks. Store both incidences of a self-loop, while marking its single edge record used only once.
Kahn's algorithm 2026-10-14
Kahn's algorithm maintains each remaining vertex's indegree, the number of its unfinished incoming directed edges, and a FIFO queue of ready vertices. Initialize the counts by scanning all directed edges and enqueue every vertex of indegree zero. Repeatedly dequeue a vertex , append it to the output, and decrement the count of each destination in 's adjacency list. Enqueue exactly when its count becomes zero. When the FIFO queue empties, accept the output if it contains all vertices; otherwise report a directed cycle. [ref]
https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/TopologicalX.java. Sedgewick and Wayne, TopologicalX: queue-based topological sorting
Computed ready-queue trace and the obstruction caused by a reverse dependency
. Details. In the displayed run, initialize the FIFO queue as and visit 's destinations in the order . Removing makes ready but leaves waiting for . Removing then makes ready. Publication becomes ready only after both and are removed. The output is ; choosing ready vertices differently can produce the other topological orders. For the modified directed graph with , the same procedure emits and then stops with still waiting on each other.
The invariant is that each stored indegree counts incoming directed edges from unprocessed vertices. A ready vertex therefore has every prerequisite already in the output. If the FIFO queue empties with vertices left, every remaining vertex has a predecessor in the remaining directed graph; the predecessor argument gives a directed cycle. The unprocessed vertices can also include tasks blocked downstream of a directed cycle, so the leftover list alone is not a list of exactly the cyclic vertices.
With adjacency lists, constant-time array access and counter updates, and constant-time FIFO queue operations, the time complexity is in Big O notation. Initialization scans all vertices and directed edges; each vertex is then enqueued at most once and each outgoing directed edge processed at most once. Counts, ready FIFO queue, and output need storage beyond the adjacency lists. Shifting an array on every dequeue invalidates this bound. [ref]
https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/TopologicalX.java. Sedgewick and Wayne, TopologicalX: time and extra-space bounds
[ref]https://raw.githubusercontent.com/kevin-wayne/algs4/master/src/main/java/edu/princeton/cs/algs4/Digraph.java. Sedgewick and Wayne, Digraph: adjacency lists and outgoing-edge iteration
The name refers to A. B. Kahn's 1962 paper "Topological sorting of large networks", published in Communications of the ACM, volume 5, issue 11, pages 558–562. Its abstract motivates topological sorting through network analysis and project planning. [ref]
https://doi.org/10.1145/368996.369025. Kahn, Topological sorting of large networks (1962): publisher's record and abstract
// Target: foundations-of-mathematics.bigb
K-core peeling invariant 2026-10-24
During repeated deletion of vertices of current degree below , every subgraph of minimum degree at least is contained in the active induced subgraph. If an active vertex has degree below , its degree in any subgraph of the active graph is no greater, so no candidate can contain it. This proves the invariant inductively. A queue of threshold crossings and adjacency lists implements peeling in time.
Leaf pruning for the 2-core 2026-10-18
Use adjacency lists and a FIFO queue to implement the deletion rule in time complexity . Compute each initial degree of a vertex once, mark every vertex alive, and put all vertices of degree of a vertex zero or one in the FIFO queue. Each vertex is inserted at most once. Repeatedly remove the front vertex , mark it dead, and traverse its original adjacency list. For each still-alive graph neighbour , decrease the stored degree of a vertex of by one; if it has just become one, append . A vertex already queued can drop from one to zero before its turn, but it stays queued and requires no second insertion. Stop when the FIFO queue is empty; the alive vertices induce .
For the displayed example, initially enqueue in that order. The successive FIFO queues, after each deletion and all its updates, areThis explains the delayed deletion of : its initial degree of a vertex was three, but the relevant degree of a vertex is always its current one in the surviving induced subgraph. Removing only the original vertices of degree of a vertex one would give the wrong answer.
Initialization inspects every vertex and every adjacency list entry once. During deletion each original adjacency list is traversed at most once, so at most entries are inspected in an undirected graph. All insertions, removals and stored-degree updates cost constant time each. Thus total time complexity is and additional storage, beyond the adjacency lists, is . The cost includes isolated vertices; repeatedly rescanning the whole surviving graph after each deletion would lose this bound. The single-threshold FIFO queue method is enough for the 2-core; the full k-core of a graph decomposition computes all thresholds.
Machine word 2026-10-14
A fixed-size unit of stored information in a computational cost model. Counting machine words separately from bits makes assumptions about identifier and value sizes explicit: for example, the storage bound for an adjacency list assumes each vertex identifier fits into a constant number of machine words.
Neighbour enumeration 2026-10-14
Neighbour enumeration reports the neighbours of a specified vertex. For a simple graph, scan its adjacency list and report each entry. With constant-time list access and work per entry, a list of length needs time in Big O notation, conventionally written for nonempty lists. Use vertex degree for an undirected graph and outdegree for a directed graph. Obtaining the list reference can take constant time; consuming it requires visiting every entry.[ref]
Digraph.java. Digraph.java, class documentation and adj: obtaining the collection versus iterating in time proportional to outdegree
In our undirected example, enumerating 's neighbours reads : three entries. Scanning row of the full adjacency matrix reads six entries, , to produce the same answer. In the directed variant, enumerating 's outgoing neighbours reads no neighbour entries, but still needs constant work to locate and recognize its empty list. For a multigraph, scanning lists enumerates incident edges with multiplicity; reporting only distinct neighbours additionally requires removing repetitions.
For a short time complexity argument, each reported entry requires at least one output operation, and one scan suffices. Across all vertices, the undirected entry count is by the handshaking lemma; the directed count is . A graph search that processes each vertex once and scans each of its entries once therefore does structural work, provided its visited-state access and FIFO queue or stack operations also have the required constant-time costs. With a full adjacency matrix, scanning every row instead inspects entries. This is how the representation enters the analysis of breadth-first search and depth-first search.










