Структура даних про дерево в C #


248

Я шукав структуру даних про дерево або графік у C #, але, мабуть, не надано жодної. Обширне вивчення структур даних за допомогою C # 2.0 трохи пояснює, чому. Чи є зручна бібліотека, яка зазвичай використовується для забезпечення цієї функціональності? Можливо, за допомогою стратегії вирішення питань, представлених у статті.

Я відчуваю себе трохи нерозумно, реалізуючи своє власне дерево, так само, як я б реалізував власний ArrayList.

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


2
Трохи більш екстремальні дерева: stackoverflow.com/questions/196294/… ;-)
Tuomas Hietanen

Чи є якась причина, що не можна включити TreeView до проекту та використовувати його? Немає причин насправді показувати це користувачеві. Звичайно, існує кілька форм проектів, коли це не варіант. Завжди можна створити нові класи, які успадковуються з прикладу TreeNode, якщо потрібна особлива складність?
Просто Г.

9
Я вважаю б поганою ідеєю імпортувати всю бібліотеку інтерфейсу користувача для дуже простого дерева.
стимули

1
Чи можете ви мотивувати? Це вже не справжня потреба в просторі жорсткого диска? Незграбний? Як я вже згадував, я можу зрозуміти, що це не рішення для спеціалізованого програмного забезпечення або чогось без існуючого користувальницького інтерфейсу. Я ледачий програміст, якщо я можу отримати структуру безкоштовно все її добре. І в існуючій бібліотеці є дуже багато безкоштовно, можна знайти багато коду від людей, які використовували її для багатьох речей.
Просто Г.

1
Ось простий тип дерева: public class Tree<T> : List<Tree<T>> { public T Value; }.
Енігмативність

Відповіді:


155

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

Основна структура, яку вам потрібно буде реалізувати, - це колекція вузлів, і ось кілька варіантів для початку роботи. Припустимо, що клас Node є базовим класом всього рішення.

Якщо вам потрібно лише переміщатися вниз по дереву, то для класу Node потрібен Список дітей.

Якщо вам потрібно перейти вгору по дереву, то для класу Node потрібна посилання на його батьківський вузол.

Створіть метод AddChild, який бере на себе всі деталі цих двох пунктів та будь-яку іншу логіку бізнесу, яку необхідно реалізувати (дочірні обмеження, сортування дітей тощо)


5
особисто я не заперечував би, щоб якесь самоврівноважуване бінарне дерево було додане до бібліотеки, оскільки це додаткова робота, ніж просто використання суміжного списку.
jk.

8
@jk Я вважаю, що SortedDictionary і SortedSet побудовані на червоних / чорних деревах, тому використовувати їх слід.
jonp

Погляньте на композитний візерунок ;-) Саме те, що ви шукаєте
Ніколя Ворон

119
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 ??


22
В цьому випадку, в C # в будь-якому випадку, ви можете не писати свій власний делегат і використовувати попередньо зроблений Action<T>делегат: public void traverse(NTree<T> node, Action<T> visitor). Дія <> 's підпис: void Action<T>( T obj ). Також є версії від 0 до 4 різних параметрів. Існує також аналогічний делегат для функцій, що називається Func<>.
Бенні Джобіган

2
як би я назвав цього делегата?
Дивовижно

3
змінити метод переходу на статичний або, можливо, обернути його, щоб приховати рекурсивну природу, було б хорошою ідеєю, але його легко пройти: створити метод з підписом делегата, тобто для дерева ints: void my_visitor_impl (int data) - зробіть його статичним, якщо вам потрібно, інстанціюйте делегат: TreeVisitor <int> my_visitor = my_visitor_impl; а потім викликати кореневий вузол або клас NTree, якщо ви зробите його статичним: NTree <int> .traverse (my_tree, my_visitor)
Aaron Gage

10
Здійснення addChild () повернення доданого NTree зробило б приємніше додавання даних до дерева. (Якщо я не пропускаю хитрий спосіб побудувати дерево з цим, не покладаючись на деталі реалізації, що нещодавно додана дитина == getChild (1)?)
Rory

1
Я думаю, що це твердження --i == 0буде функціонувати лише в одному випадку? Це правда. Це мене збентежило
Васім Ахмад Наєм

57

Ось моя, яка дуже схожа на Аарона Гейджа , просто трохи звичніша, на мій погляд. У своїх цілях я не стикався з жодними проблемами продуктивності 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()));
        }
    }
}

чому ваша властивість Value піддається, коли ви встановлюєте її в конструкторі? що залишає його відкритим для маніпуляцій ПІСЛЯ ви вже встановили це через конструктор правильно? Чи повинен бути приватний набір?
PositiveGuy

Звичайно, чому б не зробити це непорушним? Відредаговано.
Ронні Овербі

Дякую! Мені дуже сподобалося, що не треба писати своє. (Досі не можу повірити, що це не річ, яка існує споконвічно. Я завжди думав, що у .net або, принаймні, .net 4.0 було все .)
neminem

3
Мені сподобалось це рішення. Я також виявив, що мені потрібно вставити, для цього я додав наступний метод. public TreeNode<T> InsertChild(TreeNode<T> parent, T value) { var node = new TreeNode<T>(value) { Parent = parent }; parent._children.Add(node); return node; } var five = myTree.AddChild(5); myTree.InsertChild(five, 55);
JabberwockyDecompiler

48

Ще одна структура дерева:

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

БОНУС
Дивіться повноцінне дерево з:

  • ітератор
  • пошук
  • Java / C #

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


Як використовувати пошук у прикладі вашого коду? Звідки береться node? Чи означає це, що я повинен перебирати дерево, щоб використовувати код пошуку?
BadmintonCat

@GrzegorzDev Можливо -1, оскільки він не реалізує всіх IEnumerable<>членів, тому не компілюється.
Уве Кеїм

1
@UweKeim Good Job, наступного разу спробуйте скористатися кодом із фактичними умовами.
szab.kel

Єдина проблема, яку я бачу, полягає в тому, що вона не буде правильно серіалізована з базовим JsonConvert, оскільки вона реалізує IEnumerable <>
Rakiah

22

Загалом відмінна бібліотека колекцій загальної колекції C5 має кілька різних структур даних на основі дерева, включаючи набори, пакети та словники. Вихідний код доступний, якщо ви хочете вивчити їх деталі реалізації. (Я використовував колекції C5 у виробничому коді з хорошими результатами, хоча конкретно не використовував жодної структури дерева.)


7
Не знаю, чи, можливо, щось змінилося, але зараз книгу можна вільно завантажити у форматі PDF із сайту C5.
Оскар

4
Відсутність документації більше не викликає занепокоєння, оскільки існує 272 сторінок PDF, що доповнює бібліотеку ... Не можу коментувати якість коду, але, судячи з якості документа, я дуже з нетерпінням чекаю цього часу сьогодні розібратися!
Флоріан Дойон

2
Як я розумію, ця бібліотека C5 взагалі не має дерев, а лише деякі структури даних по дереву.
Ройм

10

Дивіться http://quickgraph.codeplex.com/

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


8

Якщо ви хочете написати свій власний, ви можете почати з цього шести частинного документа, в якому детально описується ефективне використання структур даних C # 2.0 та про те, як проаналізувати вашу реалізацію структур даних у C #. Кожна стаття має приклади та інсталятор із зразками, за якими ви можете слідувати.

"Широке вивчення структур даних за допомогою C # 2.0" Скотта Мітчелла


7

У мене трохи розширення до рішень.

Використовуючи рекурсивну загальну декларацію та похідний підклас, ви можете краще сконцентруватися на фактичній цілі.

Зауважте, що це відрізняється від загальної реалізації, вам не потрібно вводити "вузол" у "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);  
}  

що таке глибина і звідки і як ви її отримуєте?
PositiveGuy

@ WeDoTDD.com, дивлячись на свій клас, ви бачите, що Traverse оголошує його як 0 для початку у кореневому вузлі, а потім використовує метод traverse, додаючи до цього int кожну ітерацію.
Едвард

Як би ви шукали все дерево для того чи іншого вузла?
mattpm

6

Ось моя власна:

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

4

Спробуйте цей простий зразок.

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
}

2

Я створюю клас Node, який може бути корисним для інших людей. Клас має такі властивості, як:

  • Діти
  • Предки
  • Нащадки
  • Брати і сестри
  • Рівень вузла
  • Батьківський
  • Корінь
  • І т.д.

Існує також можливість конвертувати плоский список елементів з Id та ParentId у дерево. Вузли містять посилання як на дітей, так і на батьків, так що ітераційні вузли роблять досить швидкими.


2

Оскільки це не згадується, я хотів би звернути увагу на тепер випущену базу коду .net: конкретно, код для, SortedSetякий реалізує Red-Black-Tree:

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

Це, однак, збалансована структура дерева. Тож моя відповідь - це більше посилання на те, що я вважаю, є єдиною рідною структурою дерева в основній бібліотеці .net.


2

Я доповнив код, яким поділився @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;
        }
    }

Хороший дизайн. Однак я не впевнений, чи вузол 'є' послідовністю його дочірнього вузла. Я б врахував наступне: вузол 'має' нуль або більше дочірніх вузлів, тому вузол не походить від послідовності дочірніх вузлів, але це агрегація (склад?) Його дочірніх вузлів
Харальд Коппулзе

2

Ось Дерево

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

1

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

Скажімо, у вас є personклас, який включає в себе відомості про когось parents, чи бажаєте ви мати структуру дерева як частину вашого «доменного класу» або використовувати окремий клас дерева, який містив посилання на об’єкти вашої особи? Подумайте про таку просту операцію, як отримання всього grandchildrena person, чи повинен цей код бути у person класі, чи повинен користувач personкласу знати про окремий клас дерева?

Ще один приклад - дерево розбору в компіляторі ...

Як показують обидва ці приклади, це те, що концепція дерева є частиною домену даних, а використання окремого дерева загального призначення принаймні подвоює кількість створених об'єктів, а також ускладнює програмування API знову.

Ми хочемо - це спосіб повторного використання стандартних операцій з деревом, не потребуючи їх повторної реалізації для всіх дерев, при цьому, не вимагаючи використання стандартного класу дерев. Boost намагався вирішити цю проблему для C ++, але я ще не бачив жодного ефекту для адаптації .NET.


@Puchacz, вибачте, що мені вже 15 років не вистачає даних про C ++, перегляньте Boost і Templates, після декількох слабких досліджень ви можете їх зрозуміти. Потужність має високі витрати на навчання !!
Ян Рінроуз

1

Я додав повне рішення та приклад, використовуючи клас 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);

Чи повинен траверс стати статичним методом? Це здається дуже незграбним, як метод екземпляра переходить у себе
Sineesthetic

0

Ось моя реалізація 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();
    }
}

0

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


-4

Якщо вам потрібна реалізована структура даних з укоріненим деревом, яка використовує менше пам'яті, ви можете написати свій клас 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
}

12
Розміщення коду C ++ на запитання, спеціально для C #, не найкраща ідея, Джейк. Особливо той, що включає вказівники. Ви знаєте, що покажчики безжально полюють на C #, правда? : p
ThunderGr

2
@ThunderGr - це не справедливо. Відповідь на C # було б краще, але ці C ++-покажчики можуть бути зрозумілі C # -вимовниками як посилання (вони менш безпечні, нормально). Після того, як Девід Бойке, Аарон Гейдж, Ронні Овербі, Гжегож Дев, Береж та Ерік Нагель усі, що висунули в основному однакову структуру даних з незначними відмінностями лише у вираженні, Джейк переніс розрив пов'язаного списку, даючи простіші структури, що мають лише один тип вузла та судноплавність. Не висловлюйте свою неприязнь до C ++, конструюючи відповідь шляхом голосування.
migle

3
@migle Я не спростував відповіді (також не відмовив). І мені не подобається C ++. Я бачив, що відповідь було спровоковано, але ніхто не запропонував Джейку нічого про те, чому і як він поліпшить свою відповідь. Йдеться не про те, щоб бути кращим. Питання позначено лише для C #. Опублікувати відповіді іншою мовою, ніж тег, не рекомендується, і деякі люди подадуть заявку.
Грім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.