【问题标题】:How to implement a Non-Binary tree如何实现非二叉树
【发布时间】:2013-06-01 21:26:37
【问题描述】:

我在实现非二叉树时遇到问题,其中根节点可以有任意数量的子节点。基本上,我想要一些关于如何使用它的想法,因为我确实编写了一些代码,但我仍然坚持下一步该做什么。顺便说一句,我根本不能使用任何 Collections 类。我只能使用系统。

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

}

}

【问题讨论】:

  • 这应该是为另一个项目做准备的练习。我可以使用节点的链表吗?
  • 当然你可以使用自己的节点链接列表。
  • I can only useSystem 约束需要解释一下。还有,需要什么操作?在任意位置插入、删除?
  • 不需要移除,它唯一能做的就是在任意位置插入。
  • 提示:二叉树有两个对其他树节点的引用,称为左和右。任意树有两个对其他树节点的引用,称为 FirstChild 和 NextSibling。任意树二叉树;您只需为节点分配不同的含义。

标签: c# data-structures tree multiway-tree


【解决方案1】:

到目前为止,Jerska 的解决方案是最好的,但它不必要地复杂。

因为我认为这是一个家庭作业,所以让我给你一个方向。你想要的数据结构是:

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 度”的二叉树,其中根永远不会有“正确”的孩子。二叉树和任意树是一回事;您只需为两个孩子分配不同的含义

【讨论】:

  • 看来我会考虑这种方法。由于似乎有无数种方法可以表示任意树,我相信更直接地看待它是有意义的。谢谢您的帮助!只有一个问题,要写出树中的所有节点,我应该将所有节点添加到链表中还是没有必要?
  • @KarimOumghar:没必要;您可以轻松地编写这棵树的递归遍历,就像编写二叉树的递归遍历一样。
  • 我一直在寻找类似的解决方案,这真的很简洁,我一定会使用它。不过我有一个担忧:在您的实现中,您从下往上添加节点,我认为您的方法不允许添加新节点,因为 TreeNodes 有一个私有集,所以您不能修改现有节点来自班外以容纳新的孩子或兄弟姐妹。您如何建议使用此实现添加新节点?
  • @leoking 您可以使树可变,或者您可以重写脊椎以在插入时生成新树。与这些任意树之一相比,这对普通二叉树的效果更好,但它非常简单。
  • 通过使树可变,这是否只是意味着更改字段的访问权限?另外,在这种情况下,您所说的脊椎是什么意思?
【解决方案2】:

既然不能使用集合,为什么不创建自己的列表呢?

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

然后您将需要实现不同的功能(getter、removers 等)。但这是你的工作。

【讨论】:

    【解决方案3】:

    如果您不能使用任何集合,请将所有子元素中的链接存储到父元素。您可以使用下一个数据结构对图形进行建模:

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

    【讨论】:

    • 但是如何进行深度优先搜索呢?
    • @Jerska 你需要吗?这是明确的要求吗?为什么没有提到广度优先搜索?
    • 我不是这篇文章的作者,我想知道这是否可能。然而,我实际上对广度优先搜索也有同样的好奇。
    • @Jerska 你根本做不到。您仍然必须存储所有子元素的链接,并且处理这种树非常麻烦。但是,我仍然认为“嘿,我不能使用任何收集的东西”的要求非常不合理。
    【解决方案4】:
    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;
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      您可以使用只有下一个指针和子指针的节点类型来表示多路树。

      节点的next指针用于指向下一个兄弟孩子,实现为一个简单的链表。

      节点的child指针用于指向节点的第一个子节点。

      这里有一些示例代码演示了如何将它们组合在一起。它不包含任何错误处理,也不打算成为一个完整的解决方案,但您应该能够编译它,并在必要时在调试器下运行它以充分了解它的工作原理。

      我还添加了一个可枚举示例来展示如何迭代树节点。您可能希望使用它来产生不同顺序的结果。如果使用 enumerable 过于复杂而无法满足您的需求,您将需要编写自己的简单递归方法来访问所有节点。

      请注意,此示例中的节点类型是通用的,我仅使用它来保存字符串数据。如果你想要一个泛型类型,你可以用你想要的类型替换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);
              }
          }
      }
      

      (我知道这与上面 Eric 的答案相似,如果我在写这个之前读过那个答案,我可能不会打扰 - 但我已经写了这个,我不想只是扔它离开了。)

      【讨论】:

        【解决方案6】:

        一些随机的想法:

        • 节点需要某种子节点列表,考虑使用并发证明实现,IEnumerable&lt;NodeType&gt; 很可能符合要求
        • 您可能想要也可能不想要一个指向父级的反向指针 - 考虑一个用于快速(阅读:不太蹩脚)遍历
        • 我建议您创建一个NodeType&lt;T&gt;,以便在食用树木时更轻松

        【讨论】:

          猜你喜欢
          • 2011-02-05
          • 2012-01-09
          • 2011-01-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-04-14
          • 1970-01-01
          相关资源
          最近更新 更多