How to implement a Non-Binary tree

僤鯓⒐⒋嵵緔 提交于 2019-11-30 02:27:37

So far Jerska's solution is the best but it is needlessly complicated.

Since I assume this is a homework exercise let me give you the direction you should head in. The data structure you want is:

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

Let's now redraw your diagram -- vertical lines are "first child", horizontal lines are "next sibling"

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

Make sense?

Now, can you write code that produces this tree using this data structure? Start from the rightmost leaves and work your way towards the root:

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 ...

Notice that an arbitrary tree is just a binary tree "rotated 45 degrees", where the root never has a "right" child. Binary trees and arbitrary trees are the same thing; you just assign different meanings to the two children.

Since you can't use a collection, why don't you create your own list ?

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

And use it like this :

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

You'll now have :

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

Then you will need to implement different functions (getters, removers, etc.). But that's your job.

If you can't use any Collections, store link in all child elements to parent. You can model your graph by using next data structure:

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

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

You can now have as many childs for each node, as you like:

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

}

You can represent a multiway tree using a node type that has just a next pointer and a child pointer.

The node's next pointer is used to point to the next sibling child, implemented as a simple linked list.

The node's child pointer is used to point to the first child of the node.

Here's some sample code which demonstrates how to put this together. It does not contain any error handling and it isn't intended to be a complete solution, but you should be able to compile it and - if necessary - run it under the debugger to fully understand how it works.

I also added an example enumerable to show how you could iterate over the tree nodes. You will probably want to play around with this to produce results in different orders. IF using an enumerable is too complex for what you need, you will need to write your own simple recusive method to visit all the nodes.

Note that the node type is generic in this example, and I'm only using it to hold string data. You could just substitute T with the type you want if you don't want a generic type.

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

(I know this is similar to Eric's answer above, and if I'd read that answer before writing this one I probably wouldn't have bothered - but I'd already written this and I didn't want to just throw it away.)

Some random ideas:

  • A node will need some sort of list of the child nodes, consider using a concurrency-proof implementation, most likely IEnumerable<NodeType> will fit the bill
  • You might or might not want a backpointer to the parent - conisder one for speedy (read: not too lame) traversal
  • I recommend you create a NodeType<T> to make life easier when consuming the tree
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!