AssembleTreeSample_ja - Houzkin/TreeStructures GitHub Wiki

ノードを定義してツリーを作るサンプル。

まずは使用するノードを定義します。

public class NamedNode : GeneralTreeNode<NamedNode> {
    public NamedNode() { }
    public string Name { get; set; }
}

AddChildメソッドを使用して組み立てる。

public static partial class SampleA {
    public static void Method() {
        var A = new NamedNode { Name = "A" };
        var B = new NamedNode { Name = "B"};
        var C = new NamedNode { Name = "C"};
        var D = new NamedNode { Name = "D"};
        var E = new NamedNode { Name = "E"};
        var F = new NamedNode { Name = "F"};
        var G = new NamedNode { Name = "G" };
        var H = new NamedNode { Name = "H"};
        var I = new NamedNode { Name = "I"};
        var J = new NamedNode { Name = "J" };
        var K = new NamedNode { Name = "K" };
        var L = new NamedNode { Name = "L"};
        var M = new NamedNode { Name = "M"};
        var N = new NamedNode { Name = "N"};
        var O = new NamedNode { Name = "O"};
        var P = new NamedNode { Name = "P"};

        Console.WriteLine("Assemble using the AddChild method.");
        A.AddChild(B).AddChild(C).AddChild(D);
        B.AddChild(E).AddChild(F);
        F.AddChild(G).AddChild(H);
        C.AddChild(I);
        I.AddChild(J).AddChild(K).AddChild(L);
        D.AddChild(M);
        M.AddChild(N);
        N.AddChild(O).AddChild(P);

        Console.WriteLine(A.ToTreeDiagram(x => x.Name));
        Console.WriteLine("Displaying height, level, and node index as additional information.");
        Console.WriteLine(A.ToTreeDiagram(x => $"Name:{x.Name},Height:{x.Height()},Depth:{x.Depth()},TreeIndex:{x.TreeIndex()}"));

screenshot 14

ノードを移動させるには、AddChildによって指定したノードの子ノードとして追加できます。
ただし、ノードが循環する、または兄弟ノードと重複する場合は追加できません。

        Console.WriteLine("Move nodeN to be a child node of nodeE");
        E.AddChild(N);
        Console.WriteLine(A.ToTreeDiagram(x => x.Name));

        Console.WriteLine("Move nodeB to be a child node of nodeF (Failure: Cannot add due to cyclic relationship)");
        F.AddChild(B);
        Console.WriteLine(A.ToTreeDiagram(x => x.Name));

        Console.WriteLine("Add nodeD to nodeA (Failure: Cannot add duplicate child nodes)");
        A.AddChild(D);
        Console.WriteLine(A.ToTreeDiagram(x => x.Name));

        A.Disassemble();

screenshot 15 screenshot 16

ツリーインデックスを使用する例とAssembleAsNAryTreeメソッドを使用する例

        Console.WriteLine("Assembling from a Dictionary with specified indices.");
        var dic = new Dictionary<int[], NamedNode>() {
            [new int[] { }] = A,
            [new int[] { 0 }] = I,
            [new int[] { 0, 0 }] = C,
            [new int[] { 0, 1 }] = L,
            [new int[] { 0, 0, 0, }] = D,
            [new int[] { 0, 0, 0, 0 }] = E,
            [new int[] { 0, 0, 0, 1 }] = J,
            [new int[] { 0, 0, 0, 0, 0 }] = F,
            [new int[] { 0, 0, 0, 0, 1 }] = K,
            [new int[] { 1 }] = G,
            [new int[] { 1, 0 }] = H,
            [new int[] { 1, 1 }] = B,
            [new int[] { 1, 2 }] = M,
            [new int[] { 1, 3 }] = N,
            [new int[] { 1, 0, 0 }] = O,
            [new int[] { 1, 3, 0 }] = P,
        };
        Console.WriteLine(dic.AssembleTree().ToTreeDiagram(x => x.Name));
        Console.WriteLine("Displaying additional information such as indices assigned from parent nodes and paths.");
        Console.WriteLine(A.ToTreeDiagram(x => $"Name:{x.Name},BranchIndex:{x.BranchIndex()},NodePath:{x.NodePath(y=>y.Name)}"));

        A.Disassemble(); 
        Console.ReadLine();


        Console.WriteLine("Building a collection as an N-ary tree.");
        var root = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray().Select(x => x.ToString())
            .AssembleAsNAryTree(3, x => new NamedNode() { Name = x });
        
        Console.WriteLine(root.ToTreeDiagram(x => x.Name));
        Console.WriteLine($"Levelorder:{string.Join(",", root.Levelorder().Select(x=>x.Name))}");
        Console.WriteLine($"Preorder:{string.Join(",", root.Preorder().Select(x => x.Name))}");
        Console.WriteLine($"Postorder:{string.Join(",", root.Postorder().Select(x => x.Name))}");
        Console.WriteLine($"Inorder:{string.Join(",", root.Inorder().Select(x => x.Name))}");

        Console.WriteLine("\n ----- Pickup C node -----");
        var nodeC = root.Levelorder().First(x => x.Name == "C");
        Console.WriteLine($"Upstream:{string.Join(",", nodeC.Upstream().Select(x=>x.Name))}");
        Console.WriteLine($"Ancestors:{string.Join(",", nodeC.Ancestors().Select(x => x.Name))}");
        Console.WriteLine($"Leafs:{string.Join(", ", nodeC.Leafs().Select(x=>x.Name))}");
        Console.WriteLine($"Genarations:{string.Join(",", nodeC.Generations().Select(x => x.Name))}");
        Console.WriteLine($"Siblings:{string.Join(",", nodeC.Siblings().Select(x=>x.Name))}");
        Console.WriteLine($"Width:{nodeC.Width()}");

        Console.WriteLine("\n ----- Pickup R node -----");
        var nodeR = root.Levelorder().First(x => x.Name == "R");
        Console.WriteLine($"Upstream:{string.Join(",", nodeR.Upstream().Select(x => x.Name))}");
        Console.WriteLine($"Ancestors:{string.Join(",", nodeR.Ancestors().Select(x => x.Name))}");
        Console.WriteLine($"Leafs:{string.Join(", ", nodeR.Leafs().Select(x => x.Name))}");
        Console.WriteLine($"Genarations:{string.Join(",", nodeR.Generations().Select(x => x.Name))}");
        Console.WriteLine($"Siblings:{string.Join(",", nodeR.Siblings().Select(x => x.Name))}");
        Console.WriteLine($"Width:{nodeR.Width()}");
        Console.WriteLine($"Root:{nodeR.Root().Name}");

        Console.ReadLine();
    }
}

screenshot 17 screenshot 18

ノードパスを使用する

var pathlist = new List<NodePath<string>>() {
    new("A","BB"),
    new("A"),
    new("A","C"),
    new("A","C","D"),
    new("A","B"),
    new("A","C","E"),
    new("A","F"),
    new("A","G","O","R"),
    new("A","B","D"),
    new("A","B","D","O"),
};
var pt = pathlist.AssembleTreeByPath(x => new NamedNode() { Name = x.Last() });
Console.WriteLine(pt.ToTreeDiagram(x => x.Name));
/*
A
├ BB
├ C
│ ├ D
│ └ E
├ B
│ └ D
│   └ O
├ F
└ G
  └ O
    └ R
*/

pt.RemoveAllDescendant(a => a.Name == "D");
Console.WriteLine(pt.ToTreeDiagram(x => x.Name));
/*
A
├ BB
├ C
│ └ E
├ B
├ F
└ G
  └ O
    └ R
*/

ツリー構築に必要なすべてのノードに対するパスが含まれている場合、Dictionaryから構築することもできます。
上記、pathlistにはノードパス("A","G")が存在しないので、Dictionaryから構築した場合、それより末端のノードは追加されません。

var pathDic = pathlist.ToDictionary(x => x, x => new NamedNode() { Name = x.Last() });
var ptt = pathDic.AssembleTreeByPath();
Console.WriteLine(ptt.ToTreeDiagram(x => x.Name));
/*
A
├ BB
├ C
│ ├ D
│ └ E
├ B
│ └ D
│   └ O
└ F
*/

複数のルートが存在する場合は AssembleForestByPath を使用します。

pathlist.AddRange( new List<NodePath<string>>() { new("FF"),new("FF", "G"),});

foreach(var p in pathlist.AssembleForestByPath(x=> new NamedNode(){ Name = x.Last() })){
    Console.WriteLine(p.ToTreeDiagram(x => x.Name));
}
/*
A
├ BB
├ C
│ ├ D
│ └ E
├ B
│ └ D
│   └ O
├ F
└ G
  └ O
    └ R

FF
└ G
*/

AddChild,RemoveChildの戻り値

ノードの追加メソッドの戻り値はSelf、削除メソッドの戻り値は削除された子ノードが返ってきます。

拡張メソッドのTry(Add|Remove)Childメソッドの場合、
正常に追加された => true, self
既に追加済みまたは、追加できなかった => false, child

正常に削除された => true, child
コレクションに存在しないまたは、削除できなかった => false, self

TryRemoveOwnメソッドでは何れもselfが返ってきます。

⚠️ **GitHub.com Fallback** ⚠️