Though it is possible to replicate the picture exactly using this style of structure template:
(h : h -> h)
(root : root -> root)
(node : node -> node)
(link : root -> node)
(typedLink : h -> link)
for

A less number of links can be used:

(root : root -> root)
(h : h -> h)
(e : e -> e)
(r : r -> r)
(s : s -> s)
(i : i -> i)
(1 : 1 -> 1)
(2 : 2 -> 2)
(3 : 3 -> 3)
(4 : 4 -> 4)
(root_h_link : root -> h)
(root_h_e_link : root_h_link -> e)
(root_h_e_1_link : root_h_e_link -> 1)
(root_h_e_r_link : root_h_e_link -> r)
(root_h_e_r_s_link : root_h_e_r_link -> s)
(root_h_e_r_s_4_link : root_h_e_r_s_link -> 4)
(root_h_i_link : root_h_link -> i)
(root_h_i_s_link : root_h_i_link -> s)
(root_h_i_s_3_link : root_h_i_s_link -> 3)
(root_s_link : root -> s)
(root_s_h_link : root_s_link -> h)
(root_s_h_e_link : root_s_h_link -> e)
(root_s_h_e_2_link : root_s_h_e_link -> 2)
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Platform.Data;
using Platform.Data.Doublets;
using Platform.Data.Doublets.Memory.United.Generic;
// A doublet links store is mapped to "db.links" file:
using var links = new UnitedMemoryLinks<uint>("db.links");
// nodes
var root = links.CreatePoint();
var h_node = links.CreatePoint();
var e_node = links.CreatePoint();
var r_node = links.CreatePoint();
var s_node = links.CreatePoint();
var i_node = links.CreatePoint();
var node_1 = links.CreatePoint();
var node_2 = links.CreatePoint();
var node_3 = links.CreatePoint();
var node_4 = links.CreatePoint();
// links
var root_h_link = links.CreateAndUpdate(root, h_node);
var root_h_e_link = links.CreateAndUpdate(root_h_link, e_node);
var root_h_e_1_link = links.CreateAndUpdate(root_h_e_link, node_1);
var root_h_e_r_link = links.CreateAndUpdate(root_h_e_link, r_node);
var root_h_e_r_s_link = links.CreateAndUpdate(root_h_e_r_link, s_node);
var root_h_e_r_s_4_link = links.CreateAndUpdate(root_h_e_r_s_link, node_4);
var root_h_i_link = links.CreateAndUpdate(root_h_link, i_node);
var root_h_i_s_link = links.CreateAndUpdate(root_h_i_link, s_node);
var root_h_i_s_3_link = links.CreateAndUpdate(root_h_i_s_link, node_3);
var root_s_link = links.CreateAndUpdate(root, s_node);
var root_s_h_link = links.CreateAndUpdate(root_s_link, h_node);
var root_s_h_e_link = links.CreateAndUpdate(root_s_h_link, e_node);
var root_s_h_e_2_link = links.CreateAndUpdate(root_s_h_e_link, node_2);
// result output
Func<string, string> replaceAddressesToNames = (sourceString) => {
sourceString = Regex.Replace(sourceString, $@"\b{root}\b", "'root'");
sourceString = Regex.Replace(sourceString, $@"\b{h_node}\b", "'h'");
sourceString = Regex.Replace(sourceString, $@"\b{e_node}\b", "'e'");
sourceString = Regex.Replace(sourceString, $@"\b{r_node}\b", "'r'");
sourceString = Regex.Replace(sourceString, $@"\b{s_node}\b", "'s'");
sourceString = Regex.Replace(sourceString, $@"\b{i_node}\b", "'i'");
sourceString = Regex.Replace(sourceString, $@"\b{node_1}\b", "'1'");
sourceString = Regex.Replace(sourceString, $@"\b{node_2}\b", "'2'");
sourceString = Regex.Replace(sourceString, $@"\b{node_3}\b", "'3'");
sourceString = Regex.Replace(sourceString, $@"\b{node_4}\b", "'4'");
return sourceString;
};
void printChildren(IList<uint> link, int offset) {
var constants = links.Constants;
links.Each((innerLink) => {
if (links.GetIndex(innerLink) == links.GetIndex(link)) {
return constants.Continue;
}
Console.Write(new string('\t', offset));
Console.WriteLine(replaceAddressesToNames(links.Format(innerLink)));
printChildren(innerLink, offset + 1);
return constants.Continue;
}, new uint[] { constants.Any, links.GetIndex(link), constants.Any });
};
Console.WriteLine(replaceAddressesToNames(links.Format(root)));
printChildren(links.GetLink(root), 1);
Expected output:
('root': 'root' 'root')
(11: 'root' 'h')
(12: 11 'e')
(14: 12 'r')
(15: 14 's')
(16: 15 '4')
(13: 12 '1')
(17: 11 'i')
(18: 17 's')
(19: 18 '3')
(20: 'root' 's')
(21: 20 'h')
(22: 21 'e')
(23: 22 '2')
Execute at .NET Fiddle.