Algorithm Search⌘K

Proofs: Incidence list

These are the human proofs of Incidence list, at full length.

The incidence-list operations keep the slot invariant and report the incident edges full

This proof shows: correctness.

We use the pseudocode of the page. An edge name ee is live when alive[e]alive[e] is true. The invariant is:

(I1) Each live ee occurs exactly once in out[tail[e]]out[tail[e]], at index posout[e]pos_{out}[e], and in no other outout list. (I2) Each live ee occurs exactly once in in[head[e]]in[head[e]], at index posin[e]pos_{in}[e], and in no other inin list. (I3) No list holds a name that is not live.

The empty structure, with no edge names and empty lists, meets (I1) to (I3) with nothing to check.

Lemma 1 (swap-remove). Let LL be a list in which the name ee occurs exactly once, at index pos[e]pos[e], and in which each other name gg of LL occurs once at index pos[g]pos[g]. After SWAP-REMOVE(L,pos,e)(L, pos, e), the list LL holds exactly the names of the old LL other than ee, each once, and pos[g]pos[g] is the index of gg for each of them.

Proof. Let k=∣L∣−1k = |L| - 1 be the index of the last cell and f=L[k]f = L[k], and let i=pos[e]i = pos[e]. If i=ki = k, then f=ef = e, line L12 writes ee over itself, and line L13 removes the last cell, which holds the only copy of ee; the other names keep their cells. If i<ki < k, then f≠ef \ne e. Line L12 writes ff into cell ii and sets pos[f]=ipos[f] = i, so ff now occurs at cells ii and kk, and ee occurs nowhere. Line L13 removes cell kk. Now ff occurs once, at index i=pos[f]i = pos[f], and each other name g≠e,fg \ne e, f is in its old cell, which is below kk and is not cell ii. In both cases the claim holds. □\square

ADD-EDGE. Line L1 picks e=∣tail∣e = |tail|, a name that no row of the table had, so by (I3) no list holds ee. Line L2 appends ee to out[u]out[u] and sets posout[e]pos_{out}[e] to its index; line L3 does the same in in[v]in[v]. So ee meets (I1) and (I2) with tail[e]=utail[e] = u and head[e]=vhead[e] = v. No other cell of any list moves, so (I1) and (I2) still hold for the other live edges, and (I3) holds because ee is live.

REMOVE-EDGE. Let ee be live, u=tail[e]u = tail[e] and v=head[e]v = head[e]. By (I1), the list out[u]out[u] meets the condition of Lemma 1 for ee and posoutpos_{out}: each name in it is live by (I3), so each occurs once at its recorded index by (I1). By Lemma 1, after line L8 the list out[u]out[u] holds its old names without ee, at their recorded indices. Line L9 does the same for in[v]in[v] by (I2). These are the only lists that change, so (I1) and (I2) hold for each live edge other than ee. Line L10 makes ee not live, and ee is in no list: it was only in out[u]out[u] and in[v]in[v], by (I1) and (I2), and it was removed from both. So (I3) holds. A self-loop, with u=vu = v, needs no special case, because out[u]out[u] and in[u]in[u] are two different lists.

INCIDENT. Let vv be a vertex of an undirected graph. The edges incident to vv are the live ee with tail[e]=vtail[e] = v or head[e]=vhead[e] = v. By (I1) and (I3), out[v]out[v] holds exactly the live ee with tail[e]=vtail[e] = v, each once; by (I2) and (I3), in[v]in[v] holds exactly the live ee with head[e]=vhead[e] = v, each once. Line L5 reports each ee of out[v]out[v] with its other end head[e]head[e], and line L6 each ee of in[v]in[v] with its other end tail[e]tail[e]. So each non-loop edge at vv is reported once, and each self-loop at vv, which is in both lists, twice. The number of reports is ∣out[v]∣+∣in[v]∣=d(v)|out[v]| + |in[v]| = d(v), the degree with a self-loop counted twice. In a directed graph only line L5 runs, and it reports exactly the edges that leave vv, each once.

Sum of degrees. By (I1) to (I3), each live edge has exactly one cell in the outout lists and one in the inin lists, so ∑v(∣out[v]∣+∣in[v]∣)=2m\sum_v (|out[v]| + |in[v]|) = 2m, where mm is the number of live edges.

FIND-EDGE. In a directed graph, an edge from uu to vv is a live ee with tail[e]=utail[e] = u and head[e]=vhead[e] = v. By (I1) and (I3), each such ee is in out[u]out[u], and by (I2) and (I3), each is in in[v]in[v]. So a scan of either list for a name with the right other end finds such an edge if and only if one exists. In an undirected graph an edge between uu and vv is incident to both, so by the INCIDENT case a scan of the incident edges of either end finds it if and only if it exists.

Cost. Lines L1 to L4 are a constant number of appends, each O(1)O(1) amortized for a dynamic array. Lines L7 to L13 are a constant number of array reads and writes, so REMOVE-EDGE is O(1)O(1). INCIDENT does O(1)O(1) work for each of the d(v)d(v) cells it reads, plus O(1)O(1). FIND-EDGE reads at most the length of the shorter list, plus O(1)O(1). The structure has 2n2n lists, 2m′2m' cells and m′m' table rows, where m′≥mm' \ge m is the number of names ever given; with no removals m′=mm' = m, and the space is Θ(n+m)\Theta(n + m). □\square

Back to Incidence list