Programing

C #의 트리 데이터 구조

lottogame 2020. 4. 14. 08:22
반응형

C #의 트리 데이터 구조


C #에서 트리 또는 그래프 데이터 구조를 찾고 있었지만 제공되지 않은 것 같습니다. C # 2.0을 사용한 광범위한 데이터 구조 조사에서는 그 이유에 대해 설명합니다. 이 기능을 제공하기 위해 일반적으로 사용되는 편리한 라이브러리가 있습니까? 아마도 기사에 제시된 문제를 해결하기위한 전략 패턴을 통해서 일 것입니다.

내 ArrayList를 구현하는 것처럼 내 자신의 트리를 구현하는 것이 바보 같은 느낌이 듭니다.

불균형 할 수있는 일반 트리를 원합니다. 디렉토리 트리를 생각하십시오. C5는 근사해 보이지만 트리 구조는 노드 계층 구조를 나타내는 것보다 검색에 더 적합한 균형 잡힌 빨강-검정 트리로 구현 된 것 같습니다.


최선의 조언은 표준 트리 데이터 구조가 없다는 것입니다. 구현할 수있는 방법이 너무 많아서 모든 기반을 하나의 솔루션으로 처리하는 것은 불가능합니다. 솔루션이 구체적 일수록 주어진 문제에 적용 할 가능성이 줄어 듭니다. 심지어 LinkedList에 짜증이납니다. 순환 링크 목록을 원한다면 어떻게해야합니까?

구현해야 할 기본 구조는 노드 모음이며 시작하기위한 몇 가지 옵션이 있습니다. 클래스 Node가 전체 솔루션의 기본 클래스라고 가정하십시오.

트리를 아래로만 탐색해야하는 경우 Node 클래스에는 하위 목록이 필요합니다.

트리를 탐색해야하는 경우 Node 클래스는 상위 노드에 대한 링크가 필요합니다.

이 두 지점의 모든 세부 사항과 구현해야하는 다른 비즈니스 논리 (자식 제한, 자식 정렬 등)를 처리하는 AddChild 메서드를 작성하십시오.


delegate void TreeVisitor<T>(T nodeData);

class NTree<T>
{
    private T data;
    private LinkedList<NTree<T>> children;

    public NTree(T data)
    {
         this.data = data;
        children = new LinkedList<NTree<T>>();
    }

    public void AddChild(T data)
    {
        children.AddFirst(new NTree<T>(data));
    }

    public NTree<T> GetChild(int i)
    {
        foreach (NTree<T> n in children)
            if (--i == 0)
                return n;
        return null;
    }

    public void Traverse(NTree<T> node, TreeVisitor<T> visitor)
    {
        visitor(node.data);
        foreach (NTree<T> kid in node.children)
            Traverse(kid, visitor);
    }
}

간단한 재귀 구현 ... <40 줄의 코드 ... 클래스 외부의 트리 루트에 대한 참조를 유지하거나 다른 클래스로 래핑해야합니다.


여기 내 의견 으로는 Aaron Gage 와 매우 유사한 광산이 있습니다. 내 목적으로와 관련하여 성능 문제가 발생하지 않았습니다 List<T>. 필요한 경우 LinkedList로 쉽게 전환 할 수 있습니다.


namespace Overby.Collections
{
    public class TreeNode<T>
    {
        private readonly T _value;
        private readonly List<TreeNode<T>> _children = new List<TreeNode<T>>();

        public TreeNode(T value)
        {
            _value = value;
        }

        public TreeNode<T> this[int i]
        {
            get { return _children[i]; }
        }

        public TreeNode<T> Parent { get; private set; }

        public T Value { get { return _value; } }

        public ReadOnlyCollection<TreeNode<T>> Children
        {
            get { return _children.AsReadOnly(); }
        }

        public TreeNode<T> AddChild(T value)
        {
            var node = new TreeNode<T>(value) {Parent = this};
            _children.Add(node);
            return node;
        }

        public TreeNode<T>[] AddChildren(params T[] values)
        {
            return values.Select(AddChild).ToArray();
        }

        public bool RemoveChild(TreeNode<T> node)
        {
            return _children.Remove(node);
        }

        public void Traverse(Action<T> action)
        {
            action(Value);
            foreach (var child in _children)
                child.Traverse(action);
        }

        public IEnumerable<T> Flatten()
        {
            return new[] {Value}.Concat(_children.SelectMany(x => x.Flatten()));
        }
    }
}

또 다른 트리 구조 :

public class TreeNode<T> : IEnumerable<TreeNode<T>>
{

    public T Data { get; set; }
    public TreeNode<T> Parent { get; set; }
    public ICollection<TreeNode<T>> Children { get; set; }

    public TreeNode(T data)
    {
        this.Data = data;
        this.Children = new LinkedList<TreeNode<T>>();
    }

    public TreeNode<T> AddChild(T child)
    {
        TreeNode<T> childNode = new TreeNode<T>(child) { Parent = this };
        this.Children.Add(childNode);
        return childNode;
    }

    ... // for iterator details see below link
}

샘플 사용법 :

TreeNode<string> root = new TreeNode<string>("root");
{
    TreeNode<string> node0 = root.AddChild("node0");
    TreeNode<string> node1 = root.AddChild("node1");
    TreeNode<string> node2 = root.AddChild("node2");
    {
        TreeNode<string> node20 = node2.AddChild(null);
        TreeNode<string> node21 = node2.AddChild("node21");
        {
            TreeNode<string> node210 = node21.AddChild("node210");
            TreeNode<string> node211 = node21.AddChild("node211");
        }
    }
    TreeNode<string> node3 = root.AddChild("node3");
    {
        TreeNode<string> node30 = node3.AddChild("node30");
    }
}

보너스
다음과 같은 본격적인 나무를보십시오.

  • 반복자
  • 수색
  • 자바 / C #

https://github.com/gt4dev/yet-another-tree-structure


일반적으로 우수한 C5 Generic Collection Library 는 세트, 백 및 사전을 포함하여 여러 가지 다른 트리 기반 데이터 구조를 가지고 있습니다. 구현 세부 사항을 연구하려는 경우 소스 코드를 사용할 수 있습니다. (트리 구조를 구체적으로 사용하지는 않았지만 프로덕션 코드에서 C5 컬렉션을 사용했지만 좋은 결과를 얻었습니다.)


http://quickgraph.codeplex.com/을 참조하십시오

QuickGraph는 .Net 2.0 이상을위한 일반 지향 / 무 방향 그래프 데이터 구조 및 알고리즘을 제공합니다. QuickGraph는 깊이 우선 시치, 호흡 우선 검색, A * 검색, 최단 경로, k 최단 경로, 최대 흐름, 최소 스패닝 트리, 최소 공통 조상 등의 알고리즘을 제공합니다. QuickGraph는 MSAGL, GLEE 및 Graphviz를 지원합니다. 그래프 렌더링, GraphML 로의 직렬화 등 ...


직접 작성하려면 C # 2.0 데이터 구조의 효과적인 사용법과 C #에서 데이터 구조의 구현 분석 방법에 대해 자세히 설명하는이 6 부 문서로 시작할 수 있습니다. 각 기사에는 예제와 함께 따를 수있는 샘플이 포함 된 설치 프로그램이 있습니다.

Scott Mitchell의 “C # 2.0을 사용한 광범위한 데이터 구조 검사”


솔루션에 약간의 확장이 있습니다.

재귀 적 일반 선언과 파생 하위 클래스를 사용하면 실제 대상에 더 집중할 수 있습니다.

그것은 일반적인 구현이 아닌 것과 다르므로 'NodeWorker'에서 'node'를 캐스트 할 필요가 없습니다.

내 예는 다음과 같습니다.

public class GenericTree<T> where T : GenericTree<T> // recursive constraint  
{
  // no specific data declaration  

  protected List<T> children;

  public GenericTree()
  {
    this.children = new List<T>();
  }

  public virtual void AddChild(T newChild)
  {
    this.children.Add(newChild);
  }

  public void Traverse(Action<int, T> visitor)
  {
    this.traverse(0, visitor);
  }

  protected virtual void traverse(int depth, Action<int, T> visitor)
  {
    visitor(depth, (T)this);
    foreach (T child in this.children)
      child.traverse(depth + 1, visitor);
  }
}

public class GenericTreeNext : GenericTree<GenericTreeNext> // concrete derivation
{
  public string Name {get; set;} // user-data example

  public GenericTreeNext(string name)
  {
    this.Name = name;
  }
}

static void Main(string[] args)  
{  
  GenericTreeNext tree = new GenericTreeNext("Main-Harry");  
  tree.AddChild(new GenericTreeNext("Main-Sub-Willy"));  
  GenericTreeNext inter = new GenericTreeNext("Main-Inter-Willy");  
  inter.AddChild(new GenericTreeNext("Inter-Sub-Tom"));  
  inter.AddChild(new GenericTreeNext("Inter-Sub-Magda"));  
  tree.AddChild(inter);  
  tree.AddChild(new GenericTreeNext("Main-Sub-Chantal"));  
  tree.Traverse(NodeWorker);  
}  

static void NodeWorker(int depth, GenericTreeNext node)  
{                                // a little one-line string-concatenation (n-times)
  Console.WriteLine("{0}{1}: {2}", String.Join("   ", new string[depth + 1]), depth, node.Name);  
}  

이 간단한 샘플을 사용해보십시오.

public class TreeNode<TValue>
{
    #region Properties
    public TValue Value { get; set; }
    public List<TreeNode<TValue>> Children { get; private set; }
    public bool HasChild { get { return Children.Any(); } }
    #endregion
    #region Constructor
    public TreeNode()
    {
        this.Children = new List<TreeNode<TValue>>();
    }
    public TreeNode(TValue value)
        : this()
    {
        this.Value = value;
    }
    #endregion
    #region Methods
    public void AddChild(TreeNode<TValue> treeNode)
    {
        Children.Add(treeNode);
    }
    public void AddChild(TValue value)
    {
        var treeNode = new TreeNode<TValue>(value);
        AddChild(treeNode);
    }
    #endregion
}

다른 사람들에게 도움이 될 수 있는 Node 클래스만듭니다 . 이 클래스에는 다음과 같은 속성이 있습니다.

  • 어린이
  • 조상
  • 자손
  • 형제 자매
  • 노드의 레벨
  • 부모의
  • 뿌리
  • 기타.

Id 및 ParentId가있는 단순 항목 목록을 트리로 변환 할 수도 있습니다. 노드는 자식과 부모 모두에 대한 참조를 보유하므로 노드를 매우 빠르게 반복 할 수 있습니다.


언급되지 않았으므로 지금 출시 된 .net 코드베이스, 특히 SortedSetRed-Black-Tree를 구현하는 코드에주의를 기울이고 싶습니다 .

https://github.com/Microsoft/referencesource/blob/master/System/compmod/system/collections/generic/sortedset.cs

그러나 이것은 균형 트리 구조입니다. 그래서 내 대답은 .net 핵심 라이브러리의 유일한 기본 트리 구조라고 생각하는 것에 대한 참조입니다.


@Berezh가 공유 한 코드를 완성했습니다.

  public class TreeNode<T> : IEnumerable<TreeNode<T>>
    {

        public T Data { get; set; }
        public TreeNode<T> Parent { get; set; }
        public ICollection<TreeNode<T>> Children { get; set; }

        public TreeNode(T data)
        {
            this.Data = data;
            this.Children = new LinkedList<TreeNode<T>>();
        }

        public TreeNode<T> AddChild(T child)
        {
            TreeNode<T> childNode = new TreeNode<T>(child) { Parent = this };
            this.Children.Add(childNode);
            return childNode;
        }

        public IEnumerator<TreeNode<T>> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (IEnumerator)GetEnumerator();
        }
    }
    public class TreeNodeEnum<T> : IEnumerator<TreeNode<T>>
    {

        int position = -1;
        public List<TreeNode<T>> Nodes { get; set; }

        public TreeNode<T> Current
        {
            get
            {
                try
                {
                    return Nodes[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }


        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }


        public TreeNodeEnum(List<TreeNode<T>> nodes)
        {
            Nodes = nodes;
        }

        public void Dispose()
        {
        }

        public bool MoveNext()
        {
            position++;
            return (position < Nodes.Count);
        }

        public void Reset()
        {
            position = -1;
        }
    }

여기 나무가 있습니다

public class Tree<T> : List<Tree<T>>
{
    public  T Data { get; private set; }

    public Tree(T data)
    {
        this.Data = data;
    }

    public Tree<T> Add(T data)
    {
        var node = new Tree<T>(data);
        this.Add(node);
        return node;
    }
}

이니셜 라이저를 사용할 수도 있습니다.

    var tree = new Tree<string>("root")
    {
        new Tree<string>("sample")
        {
            "console1"
        }
    };

여기 내 것이 있습니다 :

class Program
{
    static void Main(string[] args)
    {
        var tree = new Tree<string>()
            .Begin("Fastfood")
                .Begin("Pizza")
                    .Add("Margherita")
                    .Add("Marinara")
                .End()
                .Begin("Burger")
                    .Add("Cheese burger")
                    .Add("Chili burger")
                    .Add("Rice burger")
                .End()
            .End();

        tree.Nodes.ForEach(p => PrintNode(p, 0));
        Console.ReadKey();
    }

    static void PrintNode<T>(TreeNode<T> node, int level)
    {
        Console.WriteLine("{0}{1}", new string(' ', level * 3), node.Value);
        level++;
        node.Children.ForEach(p => PrintNode(p, level));
    }
}

public class Tree<T>
{
    private Stack<TreeNode<T>> m_Stack = new Stack<TreeNode<T>>();

    public List<TreeNode<T>> Nodes { get; } = new List<TreeNode<T>>();

    public Tree<T> Begin(T val)
    {
        if (m_Stack.Count == 0)
        {
            var node = new TreeNode<T>(val, null);
            Nodes.Add(node);
            m_Stack.Push(node);
        }
        else
        {
            var node = m_Stack.Peek().Add(val);
            m_Stack.Push(node);
        }

        return this;
    }

    public Tree<T> Add(T val)
    {
        m_Stack.Peek().Add(val);
        return this;
    }

    public Tree<T> End()
    {
        m_Stack.Pop();
        return this;
    }
}

public class TreeNode<T>
{
    public T Value { get; }
    public TreeNode<T> Parent { get; }
    public List<TreeNode<T>> Children { get; }

    public TreeNode(T val, TreeNode<T> parent)
    {
        Value = val;
        Parent = parent;
        Children = new List<TreeNode<T>>();
    }

    public TreeNode<T> Add(T val)
    {
        var node = new TreeNode<T>(val, this);
        Children.Add(node);
        return node;
    }
}

산출:

Fastfood
   Pizza
      Margherita
      Marinara
   Burger
      Cheese burger
      Chili burger
      Rice burger

대부분의 트리는 처리중인 데이터로 구성됩니다.

person누군가의 세부 사항을 포함 하는 클래스가 있다고 가정 해 봅시다. parents“도메인 클래스”의 일부로 트리 구조를 갖거나 개인 객체에 대한 링크가 포함 된 별도의 트리 클래스를 사용 하시겠습니까? 모두 받고 같은 간단한 작업에 대한 생각 grandchildren의를 person이 코드에 있어야 person클래스, 또는의 사용자해야 person클래스는 별도의 트리 클래스에 대해 알고 있나요?

또 다른 예는 컴파일러의 구문 분석 트리입니다.

이 두 예가 모두 보여주는 것은 트리의 개념이 데이터 도메인일부이며 별도의 범용 트리를 사용하면 생성되는 객체 수를 두 배 이상 늘리고 API를 다시 프로그래밍하기가 더 어렵다는 것입니다.

우리가 원하는 것은 표준 트리 클래스를 사용하지 않고도 모든 트리에 대해 다시 구현할 필요없이 표준 트리 작업을 재사용하는 방법입니다. Boost는 C ++에서 이러한 유형의 문제를 해결하려고 시도했지만 .NET에 대한 효과가 적용되는 것을 아직 보지 못했습니다.


위의 NTree 클래스를 사용하여 완전한 솔루션과 예제를 추가하고 "AddChild"메소드도 추가했습니다 ...

    public class NTree<T>
    {
        public T data;
        public LinkedList<NTree<T>> children;

        public NTree(T data)
        {
            this.data = data;
            children = new LinkedList<NTree<T>>();
        }

        public void AddChild(T data)
        {
            var node = new NTree<T>(data) { Parent = this };
            children.AddFirst(node);
        }

        public NTree<T> Parent { get; private set; }

        public NTree<T> GetChild(int i)
        {
            foreach (NTree<T> n in children)
                if (--i == 0)
                    return n;
            return null;
        }

        public void Traverse(NTree<T> node, TreeVisitor<T> visitor, string t, ref NTree<T> r)
        {
            visitor(node.data, node, t, ref r);
            foreach (NTree<T> kid in node.children)
                Traverse(kid, visitor, t, ref r);
        }
    }
    public static void DelegateMethod(KeyValuePair<string, string> data, NTree<KeyValuePair<string, string>> node, string t, ref NTree<KeyValuePair<string, string>> r)
    {
        string a = string.Empty;
        if (node.data.Key == t)
        {
            r = node;
            return;
        }
    }

사용

 NTree<KeyValuePair<string, string>> ret = null;
 tree.Traverse(tree, DelegateMethod, node["categoryId"].InnerText, ref ret);

GUI에이 트리를 표시하려는 경우 TreeViewTreeNode를 사용할 수 있습니다 . (기술적으로 TreeNode를 GUI에 넣지 않고 TreeNode를 만들 수 있다고 가정하지만 간단한 자체 TreeNode 구현보다 오버 헤드가 더 많습니다.)


다음은 BST 구현입니다.

class BST
{
    public class Node
    {
        public Node Left { get; set; }
        public object Data { get; set; }
        public Node Right { get; set; }

        public Node()
        {
            Data = null;
        }

        public Node(int Data)
        {
            this.Data = (object)Data;
        }

        public void Insert(int Data)
        {
            if (this.Data == null)
            {
                this.Data = (object)Data;
                return;
            }
            if (Data > (int)this.Data)
            {
                if (this.Right == null)
                {
                    this.Right = new Node(Data);
                }
                else
                {
                    this.Right.Insert(Data);
                }
            }
            if (Data <= (int)this.Data)
            {
                if (this.Left == null)
                {
                    this.Left = new Node(Data);
                }
                else
                {
                    this.Left.Insert(Data);
                }
            }
        }

        public void TraverseInOrder()
        {
            if(this.Left != null)
                this.Left.TraverseInOrder();
            Console.Write("{0} ", this.Data);
            if (this.Right != null)
                this.Right.TraverseInOrder();
        }
    }

    public Node Root { get; set; }
    public BST()
    {
        Root = new Node();
    }
}

더 적은 메모리를 사용하는 루트 트리 데이터 구조 구현이 필요한 경우 다음과 같이 Node 클래스를 작성할 수 있습니다 (C ++ 구현).

class Node {
       Node* parent;
       int item; // depending on your needs

       Node* firstChild; //pointer to left most child of node
       Node* nextSibling; //pointer to the sibling to the right
}

참고 URL : https://stackoverflow.com/questions/66893/tree-data-structure-in-c-sharp

반응형