dndtree 0.3.1

DND-Tree dynamic connectivity data structure
Documentation
Algorithm 1: ID-Insert
 Input: a new edge (𝑒, 𝑣) and the ID-Tree index
 Output: the updated ID-Tree
 1 π‘Ÿπ‘œπ‘œπ‘‘π‘’ ← compute the root of 𝑒;
 2 π‘Ÿπ‘œπ‘œπ‘‘π‘£ ← compute the root of 𝑣;
 /* non-tree edge insertion */
 3 if π‘Ÿπ‘œπ‘œπ‘‘π‘’ = π‘Ÿπ‘œπ‘œπ‘‘π‘£ then
 4   if π‘‘π‘’π‘π‘‘β„Ž(𝑒) < π‘‘π‘’π‘π‘‘β„Ž(𝑣) then swap(𝑒, 𝑣);
 5   if π‘‘π‘’π‘π‘‘β„Ž(𝑒) βˆ’ π‘‘π‘’π‘π‘‘β„Ž(𝑣) ≀ 1 then return;
     /* reduce tree deviation */
 6   𝑀 ← 𝑒;
 7   for 1 ≀ 𝑖 < (π‘‘π‘’π‘π‘‘β„Ž(𝑒) βˆ’ π‘‘π‘’π‘π‘‘β„Ž(𝑣)) / 2 do
 8     𝑀 ← π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘(𝑀);
 9   Unlink(𝑀);
 10  Link(ReRoot(𝑒), 𝑣, π‘Ÿπ‘œπ‘œπ‘‘π‘£ );
 11  return;
 /* tree edge insertion */
 12 if 𝑠𝑑_𝑠𝑖𝑧𝑒(π‘Ÿπ‘œπ‘œπ‘‘π‘’) > 𝑠𝑑_𝑠𝑖𝑧𝑒(π‘Ÿπ‘œπ‘œπ‘‘π‘£) then
 13 swap(𝑒, 𝑣);
 14 swap(π‘Ÿπ‘œπ‘œπ‘‘π‘’, π‘Ÿπ‘œπ‘œπ‘‘π‘£);
 15 Link(ReRoot(𝑒), 𝑣, π‘Ÿπ‘œπ‘œπ‘‘π‘£);


Most processes are the same as that of D-Tree. We first conduct a connectivity
query to identify if two vertices are in the same tree. Lines 3–11 apply the
BFS heuristic for non-tree edge insertion. When the depth gap between 𝑒 and 𝑣
is over 1, a major difference from D-Tree is about the strategy of BFS heuristic in
Line 7. D-Tree uses the threshold π‘‘π‘’π‘π‘‘β„Ž(𝑒) βˆ’ π‘‘π‘’π‘π‘‘β„Ž(𝑣) βˆ’ 2 instead of π‘‘π‘’π‘π‘‘β„Ž (𝑒) βˆ’π‘‘π‘’π‘π‘‘β„Ž(𝑣)2 . To reduce
the average depth, we add half vertices from 𝑒 to its ancestor with the same depth of 𝑣 to the

- ReRoot(𝑒) rotates the tree and makes 𝑒 as the new root. It updates the parent-child relationship
  and the subtree size attribute from 𝑒 to the original root. The time complexity of ReRoot() is 𝑂(π‘‘π‘’π‘π‘‘β„Ž(𝑒)).

ReRoot from Algorithm 1 of Dynamic Spanning Trees for Connectivity Queries on Fully-dynamic Undirected Graphs (Extended Version):
Algorithm: reroot(nw)
input : tree node nw of D-tree with the root r
output : nw, new root of the rerooted D-tree
1 ch = nw; cur = nw.parent; nw.parent = NULL;
2 while cur, NULL do
3   g = cur.parent
4   cur.parent = ch
5   remove ch from cur.children
6   add cur to ch.children
7   ch = cur; cur = g;
8 while ch.parent, NULL do
9   ch.size = ch.size - ch.parent.size
10  ch.parent.size = ch.parent.size + ch.size
11  ch = ch.parent
12 return uw

- Link(𝑒, 𝑣, π‘Ÿπ‘œπ‘œπ‘‘π‘£) adds a tree 𝑇𝑒 rooted in 𝑒 to the children of 𝑣. π‘Ÿπ‘œπ‘œπ‘‘π‘£ is the root of 𝑣. Given that
  the subtree size of 𝑣 is changed, it updates the subtree size for each vertex from 𝑣 to the root.
  We apply the centroid heuristic by recording the first vertex with a subtree size larger than 𝑠𝑑_𝑠𝑖𝑧𝑒(π‘Ÿπ‘œπ‘œπ‘‘π‘£)/2.
  If such a vertex is found, we reroot the tree, and the operator returns the new root.
  The time complexity of Link() is 𝑂(π‘‘π‘’π‘π‘‘β„Ž(𝑣)).

Link from Algorithm 6 of Dynamic Spanning Trees for Connectivity Queries on Fully-dynamic Undirected Graphs (Extended Version):
Algorithm: link(𝑛𝑒, π‘Ÿπ‘’, 𝑛𝑣)
input : a node 𝑛𝑒 in D-tree 𝐷 with the root π‘Ÿπ‘’, the root 𝑛𝑣 of a D-tree currently not connected to 𝐷 via a tree edge
output : merged D-tree with new tree edge (𝑛𝑒, 𝑛𝑣)
1 add 𝑛𝑣 to 𝑛𝑒.π‘β„Žπ‘–π‘™π‘‘π‘Ÿπ‘’π‘›
2 𝑛𝑣.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ = 𝑛𝑒
3 π‘š = 𝑁𝑒𝑙𝑙; // new centroid
4 𝑖 = 𝑛𝑒
5 while 𝑖 β‰  𝑁𝑒𝑙𝑙 do
6   𝑖.𝑠𝑖𝑧𝑒 = 𝑖.𝑠𝑖𝑧𝑒 + 𝑛𝑣.𝑠𝑖𝑧𝑒
7   if 𝑖.𝑠𝑖𝑧𝑒> (π‘Ÿπ‘’.𝑠𝑖𝑧𝑒 + 𝑛𝑣.𝑠𝑖𝑧𝑒)/2 and π‘š == 𝑁𝑒𝑙𝑙 then π‘š = 𝑖;
8   𝑖 = 𝑖.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘
9 if π‘š β‰  𝑁𝑒𝑙𝑙 and π‘š β‰  π‘Ÿπ‘’ then π‘Ÿπ‘’

- Unlink(𝑒) disconnect the subtree of 𝑒 from the original tree.
  All ancestors of 𝑒 are scanned to update the subtree size.
  The time complexity of Unlink() is 𝑂 (π‘‘π‘’π‘π‘‘β„Ž(𝑒)).

UnLink from Algorithm 7 of Dynamic Spanning Trees for Connectivity Queries on Fully-dynamic Undirected Graphs (Extended Version):
Algorithm: unlink(𝑒)
input : a non-root node 𝑒 in D-tree 𝐷
output : two D-trees, not connected via tree edges
1 𝑖 = 𝑒
2 while 𝑖.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ β‰  𝑁𝑒𝑙𝑙 do
3   𝑖 = 𝑖.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘
4   𝑖.𝑠𝑖𝑧𝑒 = 𝑖.𝑠𝑖𝑧𝑒 βˆ’ 𝑒.𝑠𝑖𝑧𝑒
5 remove 𝑒 from 𝑒.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘.π‘β„Žπ‘–π‘™π‘‘π‘Ÿπ‘’π‘›
6 𝑒.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ = 𝑁𝑒𝑙𝑙
7 return (𝑒, 𝑖)


Algorithm 2: ID-Delete
Input: an existing edge (𝑒, 𝑣) and the ID-Tree
Output: the updated ID-Tree
1 if π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘(𝑒) β‰  𝑣 ∧ π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘(𝑣) β‰  𝑒 then return;
2 if π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘(𝑣) = 𝑒 then swap(𝑒, 𝑣);
3 π‘Ÿπ‘œπ‘œπ‘‘π‘£ ← Unlink(𝑒);
/* reduce the worst-case time complexity of searching replacement edge in subtree */
4 if 𝑠𝑑_𝑠𝑖𝑧𝑒(π‘Ÿπ‘œπ‘œπ‘‘π‘£) < 𝑠𝑑_𝑠𝑖𝑧𝑒(𝑒) then swap(𝑒, π‘Ÿπ‘œπ‘œπ‘‘π‘£);
/* search subtree rooted in 𝑒 */
5 𝑄 ← an empty queue, 𝑄.π‘π‘’π‘ β„Ž(𝑒);
6 𝑆 ← {𝑒};
/* 𝑆 maintains all visited vertices */
7 while 𝑄 β‰  βˆ… do
8   π‘₯ ← 𝑄.π‘π‘œπ‘ ();
9   foreach 𝑦 ∈ 𝑁 (π‘₯) do
10    if 𝑦 = π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ (π‘₯) then continue;
11    else if π‘₯ = π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ (𝑦) then
12      𝑄.π‘π‘’π‘ β„Ž(𝑦);
13      𝑆 ← 𝑆 βˆͺ {𝑦};
14    else
15      𝑠𝑒𝑐𝑐 ← true;
16      foreach 𝑀 from 𝑦 to the root do
17      if 𝑀 ∈ 𝑆 then
18        𝑠𝑒𝑐𝑐 ← false;
19        break;
20      else
21        𝑆 ← 𝑆 βˆͺ {𝑀 };
22      if 𝑠𝑒𝑐𝑐 then
23        π‘Ÿπ‘œπ‘œπ‘‘π‘£ ← Link(ReRoot(π‘₯), 𝑦, π‘Ÿπ‘œπ‘œπ‘‘π‘£ );
24        return;

Algorithm 3: Disjoint-set operators
1 Procedure Find(π‘₯)
2 if π‘₯.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ β‰  π‘₯ then
3   π‘₯.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ ← Find(π‘₯);
4   return π‘₯.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘;
5 return π‘₯;

6 Procedure Union(π‘₯, 𝑦)
7 π‘₯ ← Find(π‘₯);
8 𝑦 ← Find(𝑦);
9 if π‘₯ = 𝑦 then return;
10 if π‘₯.𝑠𝑖𝑧𝑒 > 𝑦.𝑠𝑖𝑧𝑒 then swap(π‘₯, 𝑦);
11 π‘₯.π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ ← 𝑦;
12 𝑦.𝑠𝑖𝑧𝑒 ← π‘₯.𝑠𝑖𝑧𝑒 + 𝑦.𝑠𝑖𝑧𝑒;

Double Linked List
DSnode
- 𝑖𝑑 // id of the corresponding vertex;
- π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ // pointer to the parent’s DSnode in 𝐷𝑆-tree;
- π‘π‘Ÿπ‘’ // previous pointer in the DLL of the parent’s children;
- 𝑛𝑒π‘₯𝑑 // next pointer in the DLL of the parent’s children;
- π‘β„Žπ‘–π‘™π‘‘π‘Ÿπ‘’π‘› // start position of the DDL of children.

Algorithm 4: DS-Tree operators
1 Procedure UnlinkDS(𝑒)
2 DSnode(𝑒).π‘π‘Ÿπ‘’.𝑛𝑒π‘₯𝑑 ← DSnode(𝑒).𝑛𝑒π‘₯𝑑;
3 DSnode(𝑒).𝑛𝑒π‘₯𝑑.π‘π‘Ÿπ‘’ ← DSnode(𝑒).π‘π‘Ÿπ‘’;
4 DSnode(𝑒).π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ ← DSnode(𝑒);
5 DSnode(𝑒).π‘π‘Ÿπ‘’ ← Null;
6 DSnode(𝑒).𝑛𝑒π‘₯𝑑 ← Null;

7 Procedure LinkDS(𝑒, 𝑣)
/* union without find and comparing size */
/* the input satisfies 𝑠𝑑_𝑠𝑖𝑧𝑒 (𝑒) ≀ 𝑠𝑑_𝑠𝑖𝑧𝑒 (𝑣) */
/* union two DS-Trees */
8 DSnode(𝑒).π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ ← DSnode(𝑣);
/* add 𝑒 to the new DLL */
9 DSnode(𝑒).π‘π‘Ÿπ‘’ ← DSnode(𝑣).π‘β„Žπ‘–π‘™π‘‘π‘Ÿπ‘’π‘›;
10 DSnode(𝑒).𝑛𝑒π‘₯𝑑 ← DSnode(𝑣).π‘β„Žπ‘–π‘™π‘‘π‘Ÿπ‘’π‘›.𝑛𝑒π‘₯𝑑;
11 DSnode(𝑒).π‘π‘Ÿπ‘’.𝑛𝑒π‘₯𝑑 ← DSnode(𝑒);
12 DSnode(𝑒).𝑛𝑒π‘₯𝑑 .π‘π‘Ÿπ‘’ ← DSnode(𝑒);

13 Procedure FindDS(𝑒)
14 if DSnode(𝑒).π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘ β‰  DSnode(𝑒) then
15   π‘Ÿπ‘œπ‘œπ‘‘ ← FindDS(DSnode(𝑒).π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘.𝑖𝑑);
16   UnlinkDS(𝑒);
17   LinkDS(𝑒, π‘Ÿπ‘œπ‘œπ‘‘);
18   return π‘Ÿπ‘œπ‘œπ‘‘;
19 return 𝑒;

20 Procedure Isolate(𝑒)
/* assign children of 𝑒 to the root */
21 π‘Ÿπ‘œπ‘œπ‘‘π‘’ ← FindDS(𝑒);
22 UnlinkDS(𝑒);
23 foreach child 𝑀 of 𝑒 in 𝐷𝑆-Tree do
24   UnlinkDS(𝑀);
25   LinkDS(𝑀, π‘Ÿπ‘œπ‘œπ‘‘π‘’ );

26 Procedure ReRootDS(𝑒)
27 π‘Ÿπ‘œπ‘œπ‘‘π‘’ ← FindDS(𝑒);
28 swap(DSnode(𝑒), DSnode(π‘Ÿπ‘œπ‘œπ‘‘π‘’));
29 DSnode(𝑒).𝑖𝑑 ← 𝑒;
30 DSnode(π‘Ÿπ‘œπ‘œπ‘‘π‘’).𝑖𝑑 ← π‘Ÿπ‘œπ‘œπ‘‘π‘’;

Algorithm 5: DND-Insert
Input: an existing edge (𝑒, 𝑣) and the DND-Trees index
Output: the updated DND-Trees
1 π‘Ÿπ‘œπ‘œπ‘‘π‘’ ← FindDS(𝑒);
2 π‘Ÿπ‘œπ‘œπ‘‘π‘£ ← FindDS(𝑣);
  /* 3 Lines 3–14 of Algorithm 1; */
  /* non-tree edge insertion */
  3 if π‘Ÿπ‘œπ‘œπ‘‘π‘’ = π‘Ÿπ‘œπ‘œπ‘‘π‘£ then
  4   if π‘‘π‘’π‘π‘‘β„Ž(𝑒) < π‘‘π‘’π‘π‘‘β„Ž(𝑣) then swap(𝑒, 𝑣);
  5   if π‘‘π‘’π‘π‘‘β„Ž(𝑒) βˆ’ π‘‘π‘’π‘π‘‘β„Ž(𝑣) ≀ 1 then return;
      /* reduce tree deviation */
  6   𝑀 ← 𝑒;
  7   for 1 ≀ 𝑖 < (π‘‘π‘’π‘π‘‘β„Ž(𝑒) βˆ’ π‘‘π‘’π‘π‘‘β„Ž(𝑣)) / 2 do
  8     𝑀 ← π‘π‘Žπ‘Ÿπ‘’π‘›π‘‘(𝑀);
  9   Unlink(𝑀);
  10  Link(ReRoot(𝑒), 𝑣, π‘Ÿπ‘œπ‘œπ‘‘π‘£ );
  11  return;
  /* tree edge insertion */
  12 if 𝑠𝑑_𝑠𝑖𝑧𝑒(π‘Ÿπ‘œπ‘œπ‘‘π‘’) > 𝑠𝑑_𝑠𝑖𝑧𝑒(π‘Ÿπ‘œπ‘œπ‘‘π‘£) then
  13   swap(𝑒, 𝑣);
  14   swap(π‘Ÿπ‘œπ‘œπ‘‘π‘’, π‘Ÿπ‘œπ‘œπ‘‘π‘£);
4 ReRoot(𝑒);
5 LinkDS(π‘Ÿπ‘œπ‘œπ‘‘π‘’, π‘Ÿπ‘œπ‘œπ‘‘π‘£);
6 Link(𝑒, 𝑣, π‘Ÿπ‘œπ‘œπ‘‘π‘£);

Algorithm 6: DND-Delete
Input: an existing edge (𝑒, 𝑣) and the DND-Trees index
Output: the updated DND-Trees
1 (𝑒, π‘Ÿπ‘œπ‘œπ‘‘π‘£, 𝑠𝑒𝑐𝑐, 𝑆) ← ID-Delete(𝑒, 𝑣);
2 ReRootDS(π‘Ÿπ‘œπ‘œπ‘‘π‘£);
3 if 𝑠𝑒𝑐𝑐 then return;
/* 𝑒 is the root of the smaller ID-Tree */
4 Isolate(𝑒);
5 𝑆 ← 𝑆 \ {𝑒};
6 foreach 𝑀 ∈ 𝑆 do
7   Isolate(𝑀);
8   LinkDS(𝑀, 𝑒);