Структура данных дерева в С#

Я искал структуру данных дерева или графа в С#, но я думаю, что этого не предусмотрено. Обширная проверка структур данных с использованием С# 2.0 объясняет, почему. Есть ли удобная библиотека, которая обычно используется для обеспечения этой функции? Возможно, с помощью шаблона стратегии, чтобы решить проблемы, представленные в статье.

Я чувствую себя немного глупо, реализуя свое собственное дерево, точно так же, как я реализую свой собственный ArrayList.

Я просто хочу, чтобы общее дерево было неуравновешенным. Подумайте о дереве каталогов. C5 выглядит элегантно, но их древовидные структуры, похоже, реализованы в виде сбалансированных красно-черных деревьев, которые лучше подходят для поиска, чем представляют иерархию узлов.

Ответ 1

Мой лучший совет будет заключаться в том, что нет стандартной структуры данных дерева, потому что существует так много способов реализовать ее, что невозможно было бы охватить все базы одним решением. Более конкретное решение, тем менее вероятно, что оно применимо к любой заданной проблеме. Меня даже раздражает LinkedList - что, если мне нужен круговой связанный список?

Основной структурой, которую вам нужно реализовать, будет сбор узлов, и вот несколько вариантов, чтобы вы начали. Предположим, что класс Node является базовым классом всего решения.

Если вам нужно только перемещаться по дереву, то классу Node нужен список детей.

Если вам нужно перемещаться по дереву, то классу Node требуется ссылка на его родительский node.

Создайте метод AddChild, который позаботится обо всех мелочах этих двух точек и любой другой бизнес-логики, которая должна быть реализована (ограничения для детей, сортировка дочерних элементов и т.д.)

Ответ 2

Я ненавижу это признавать, но в итоге я написал свой собственный древовидный класс, используя связанный список. На несвязанной ноте я только что обнаружил эту круглую вещь, которая при прикреплении к вещи, которую я называю "осью", позволяет легче транспортировать товары.

Ответ 3

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 строк кода... Вам просто нужно сохранить ссылку на корень дерева вне класса, или обернуть его в другой класс, возможно переименовать в TreeNode??

Ответ 4

Здесь мой, который очень похож на 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()));
        }
    }
}

Ответ 5

Еще одна древовидная структура:

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");
    }
}

BONUS
Посмотрите на полноценное дерево с помощью:

  • итератора
  • поиск
  • Java/С#

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

Ответ 6

В целом отличная C5 Generic Collection Library имеет несколько разных древовидных структур данных, включая наборы, сумочки и словари. Исходный код доступен, если вы хотите изучить их детали реализации. (Я использовал коллекции C5 в производственном коде с хорошими результатами, хотя я не использовал ни одну из древовидных структур.)

Ответ 7

См. http://quickgraph.codeplex.com/

QuickGraph предоставляет общие диаграммы направленного/неориентированного графа и алгоритмы для .Net 2.0 и выше. QuickGraph поставляется с такими алгоритмами, как первый поиск глубины, первый поиск в воздухе, поиск A *, кратчайший путь, k-кратчайший путь, максимальный поток, минимальное связующее дерево, наименее распространенные предки и т.д. QuickGraph поддерживает MSAGL, GLEE и Graphviz для визуализировать графики, сериализовать на GraphML и т.д.

Ответ 8

Если вы хотите написать свой собственный, вы можете начать с этого документа из шести частей, подробно описывающего эффективное использование структур данных С# 2.0 и как проанализировать вашу реализацию структур данных на С#. В каждой статье есть примеры и установщик с образцами, с которыми вы можете следовать.

"Обширный анализ структур данных с использованием С# 2.0" Скотта Митчелла

Ответ 9

У меня есть небольшое расширение для решений.

Используя рекурсивную общую декларацию и подкласс подбора, вы можете лучше сконцентрироваться на своей фактической цели.

Обратите внимание, что это отличается от не универсальной реализации, вам не нужно бросать 'node' в 'NodeWorker'.

Вот мой пример:

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);  
}  

Ответ 10

Попробуйте этот простой пример.

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
}

Ответ 11

Я создаю класс Node, который может быть полезен для других людей. Класс имеет такие свойства, как:

  • Дети
  • Предки
  • Потомки
  • Siblings
  • Уровень node
  • Родитель
  • Root
  • Etc.

Существует также возможность преобразования плоского списка элементов с идентификатором и ParentId в дерево. Узлы содержат ссылку как на дочерние, так и на родительские, что делает итерации узлов довольно быстрыми.

Ответ 12

Поскольку он не упоминается, я хотел бы обратить внимание на теперь выпущенную .net-кодировку .net: в частности, код для SortedSet, который реализует Red-Black-Tree:

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

Это, однако, сбалансированная древовидная структура. Поэтому мой ответ скорее является ссылкой на то, что я считаю единственной собственной древовидной структурой в .net-библиотеке.

Ответ 13

Я закончил код, который поделился @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;
        }
    }

Ответ 14

Здесь Дерево

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"
        }
    };

Ответ 15

Большинство деревьев формируются данными, которые вы обрабатываете.

Предположим, что у вас есть класс person, который содержит сведения о некоторых parents, вы бы предпочли иметь древовидную структуру как часть своего "класс домена" или использовать отдельный древовидный класс, содержащий ссылки на ваш объект человека? Подумайте о простой операции, такой как получение всех grandchildren a person, если этот код находится в personкласса, или если пользователь класса person должен знать о отдельный древовидный класс?

Другим примером является дерево разбора в компиляторе...

Как показывают оба этих примера, концепция дерева является частью домена данных, а использование отдельного дерева общего назначения, по крайней мере, удваивает количество создаваемых объектов как сделать API сложнее запрограммировать снова.

То, что мы хотим, - это способ повторного использования стандартных действий дерева, без необходимости повторного их реализации для всех деревьев, в то же время не имея необходимости использовать стандартный древовидный класс. Boost попытался решить этот тип проблемы для С++, но я пока не вижу никакого эффекта для .NET, адаптированного.

Ответ 16

Если вы собираетесь отображать это дерево в графическом интерфейсе, вы можете использовать TreeView и TreeNode. (Я предполагаю, что технически вы можете создать TreeNode, не помещая его в графический интерфейс, но у него больше накладных расходов, чем простая реализация HomeNow TreeNode.)

Ответ 17

Я добавил полное решение и пример, используя класс 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);

Ответ 18

Вот моя реализация 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();
    }
}

Ответ 19

Если вам нужна реализация структуры корневой структуры дерева, которая использует меньше памяти, вы можете написать свой класс Node следующим образом (реализация на С++):

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
}