Как реализовать Недвоичное дерево

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

using System;

namespace alternate_solution
{
 //            [root]
 //        /  /          
 //    text  text  text  text

class Node//not of type TreeNode (since Node is different from TreeNode)
{
    public string data;
    public Node child;

    public Node(string data)
    {
        this.data = data;
        this.child = null;
    }

}

} enter image description here

6 ответов


пока решение Йерска является лучшим, но это излишне сложным.

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

class TreeNode
{
  public string Data { get; private set; }
  public TreeNode FirstChild { get; private set; }
  public TreeNode NextSibling { get; private set; }
  public TreeNode (string data, TreeNode firstChild, TreeNode nextSibling)
  {
    this.Data = data;
    this.FirstChild = firstChild;
    this.NextSibling = nextSibling;
  }
}

Давайте теперь перерисуем вашу диаграмму-вертикальные линии - "первый ребенок", горизонтальные линии - "следующий брат"

Root
 |
 p1 ----- p2 ----- p4 ----- p6  
 |        |         |       |
 c1       p3       c4       p7
          |                 |
          c2 - c3           c5

смысл?

Теперь вы можете написать код, который создает это дерево, Используя эту структуру данных? начните с самых правых листьев и проложите свой путь к корню:

TreeNode c5 = new TreeNode("c5", null, null);
TreeNode p7 = new TreeNode("p7", c5, null);
TreeNode p6 = new TreeNode("p6", p6, null);
... you do the rest ...

обратите внимание, что произвольное дерево - это просто двоичное дерево, "повернутое на 45 градусов", где корень никогда не имеет" правильного " ребенка. Бинарные деревья и произвольные деревья то же самое; вы просто назначаете разные смыслов двум детям.


поскольку вы не можете использовать коллекцию, почему бы вам не создать ваш собственный список ?

class Child {
    Node node;
    Child next = null;

    public Child (Node node) {
        this.node = node;
    }

    public void addChild (Node node) {
        if (this.next == null)
            this.next = new Child (node);
        else
            this.next.addChild (node);
    }
}

class Node {
   public string data;
   public Child children = null;

   public Node (string data) {
       this.data = data;
   }

   public void addChild (Node node) {
       if (this.children == null)
           this.children = new Child (node);
       else
           this.children.addChild (node);
   }
}

и используйте его так:

Node root = new Node ("Hey");
root.addChild (new Node ("you"));
root.addChild (new Node ("me"));

вы получите :

          Node ("Hey")
        /             \
   Node ("you")     Node ("me")

тогда вам нужно будет реализовать различные функции (геттеры, удалители и т. д.). Но это твоя работа.


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

class Node
{
    public string Data { get; set; }
    public Node Parent { get; set; }

    public Node(string data, Node parent)
    {
        Data = data;
        Parent = parent;
    }
}

теперь вы можете иметь столько детей для каждого узла, как вам нравится:

var root = new Node("root", null);

var parent = new Node("parent", root);
var anotherParent = new Node("yetAnotherParent", root);

var child = new Node("Child", parent);
var anotherchild = new Node("anotherchild", parent);

class Node
{
    public string data;
    public Node parent;
    public IEnumerable<Node> children;

    public Node(string data, Node parent, IEnumerable<Node> children)
    {
        this.data = data;
        this.parent = parent;
        this.children = children;
    }

}

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

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

узла child указатель используется для указания на первый дочерний элемент узла.

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

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

обратите внимание, что тип узла generic в этом примере, и я использую его только для хранения строковых данных. Вы могли бы просто заменить T С типом, который вы хотите, если вы не требуется общий тип.

using System;
using System.Collections;
using System.Collections.Generic;

namespace Demo
{
    sealed class Node<T>
    {
        public T Data;  // Payload.

        public Node<T> Next;  // This will point to the next sibling node (if any), forming a linked-list.
        public Node<T> Child; // This will point to the first child node (if any).
    }

    sealed class Tree<T>: IEnumerable<T>
    {
        public Node<T> Root;

        public Node<T> AddChild(Node<T> parent, T data)
        {
            parent.Child = new Node<T>
            {
                Data = data,
                Next = parent.Child // Prepare to place the new node at the head of the linked-list of children.
            };

            return parent.Child;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return enumerate(Root).GetEnumerator();
        }

        private IEnumerable<T> enumerate(Node<T> root)
        {
            for (var node = root; node != null; node = node.Next)
            {
                yield return node.Data;

                foreach (var data in enumerate(node.Child))
                    yield return data;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    class Program
    {
        void run()
        {
            var tree = new Tree<string>();

            tree.Root = new Node<string>{Data = "Root"};

            var l1n3 = tree.AddChild(tree.Root, "L1 N3");
            var l1n2 = tree.AddChild(tree.Root, "L1 N2");
            var l1n1 = tree.AddChild(tree.Root, "L1 N1");

            tree.AddChild(l1n1, "L2 N1 C3");
            tree.AddChild(l1n1, "L2 N1 C2");
            var l2n1 = tree.AddChild(l1n1, "L2 N1 C1");

            tree.AddChild(l1n2, "L2 N2 C3");
            tree.AddChild(l1n2, "L2 N2 C2");
            tree.AddChild(l1n2, "L2 N2 C1");

            tree.AddChild(l1n3, "L2 N3 C3");
            tree.AddChild(l1n3, "L2 N3 C2");
            tree.AddChild(l1n3, "L2 N3 C1");

            tree.AddChild(l2n1, "L3 N1 C3");
            tree.AddChild(l2n1, "L3 N1 C2");
            tree.AddChild(l2n1, "L3 N1 C1");

            tree.Print();
        }

        static void Main()
        {
            new Program().run();
        }
    }

    static class DemoUtil
    {
        public static void Print(this object self)
        {
            Console.WriteLine(self);
        }

        public static void Print(this string self)
        {
            Console.WriteLine(self);
        }

        public static void Print<T>(this IEnumerable<T> self)
        {
            foreach (var item in self)
                Console.WriteLine(item);
        }
    }
}

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


некоторые случайные идеи:

  • узлу понадобится какой-то список дочерних узлов, рассмотрите возможность использования реализации, защищенной от параллелизма, скорее всего IEnumerable<NodeType> подойдет по счету
  • вы можете или не хотите, чтобы backpointer к родителю-conisder один для быстрого (читай: не слишком хромой) обхода
  • я рекомендую вам создать NodeType<T> чтобы облегчить жизнь при употреблении дерева