2013-06-01 4 views
6

루트 노드가 임의의 양의 자식 노드를 가질 수있는 비 이진 트리를 구현하는 데 문제가 있습니다. 기본적으로, 나는 어떤 코드를 작성 했으므로 어디로 가야하는지에 대한 아이디어를 원하지만, 다음에해야할 일에 관해서는이 시점에서 붙어있다. BTW 나는 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; 
    } 

} 

} enter image description here

+0

노드 자식 대신리스트 을 사용하지 않는 이유는 무엇입니까? – Jerska

+0

Collections 클래스를 사용할 수 없습니다. 나는 이것을 구현하기 위해서만 시스템을 사용할 수있다. –

+0

'나는 Collections 클래스를 사용하지 않습니다. 왜 그런가요? 숙제 나 면접 과제입니까? –

답변

28

지금까지 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도 회전".이진 나무와 임의의 나무는 같은 것; 두 아이들에게 다른 의미을 할당하면됩니다.

+0

이 방법을 고려할 것 같습니다. 임의의 나무를 표현하는 방법은 무수히 많은 것처럼 보이기 때문에 좀 더 직관적으로 살펴 보는 것이 바람직하다고 생각합니다. 도와 주셔서 감사합니다! 트리에있는 모든 노드를 작성하려면 단 한 가지 질문 만하십시오. 링크 된 목록에 모든 노드를 추가해야합니까, 아니면 필요하지 않습니까? –

+1

@ 카림 음 할거 : 그건 불필요합니다. 당신은이 트리의 재귀 적 순회를 쉽게 작성할 수있다. –

+0

나는 비슷한 솔루션을 찾고 있었는데, 정말 멋지다. 확실히 사용할 것이다. 그래도 한 가지 염려가 있습니다. 구현시 상향식에서 노드를 추가하면 트리 노드에 개인 집합이 있으므로 기존 노드를 수정할 수 없기 때문에 메서드에서 새 노드를 추가 할 수 있다고 생각하지 않습니다. 새로운 어린이 또는 형제 자매를 수용하기 위해 수업 외부에서. 이 구현을 사용하여 새 노드를 추가 할 것을 제안 하시겠습니까? – Lou

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

} 
2

당신이 부모에 대한 모든 자식 요소의 모든 컬렉션, 매장 링크를 사용할 수없는 경우. 당신은 다음 데이터 구조를 사용하여 그래프를 모델링 할 수 있습니다 :

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

그렇다면 어떻게 깊이 우선 검색을 할 수 있습니까? – Jerska

+0

@Jerska 필요하십니까? 그것은 명백한 요구 사항입니까? 왜 폭스 - 우선 검색에 대해 언급하지 않으셨습니까? –

+0

나는이 게시물의 저자가 아니며 가능할 수 있는지 궁금해하고있었습니다. 그러나 나는 실제로 폭스 - 우선 검색으로 동일한 경이로움을 가지고 있습니다. – Jerska

0

일부 무작위 아이디어 :

  • 을 노드는 일종의 자식 노드 목록을 필요로합니다. 동시성이 보장되는 구현을 사용하는 것이 좋습니다. IEnumerable<NodeType>은 청구서에 부합합니다.
  • 수도 있고 부모에 백 포인터하지 않을 수 있습니다 -에 대한 conisder 하나를 (읽기 : 너무 절름발이되지 않음) 빠른 탐색
  • 나는 당신이 '당신이 할 수 있기 때문에 나무
3

을 소비 할 때의 인생을 더 쉽게 만들기 위해 NodeType<T>를 작성하는 것이 좋습니다 컬렉션을 사용하지 마십시오. 왜 자신 만의 목록을 만들지 않습니까? 이 같은

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

그런 다음 당신은 다른 기능 (게터, 제거제 등)를 구현해야합니다. 그러나 그것이 당신의 일입니다.

1

다음 포인터와 자식 포인터가있는 노드 유형을 사용하여 다중 경로 트리를 나타낼 수 있습니다.

노드의 next 포인터는 다음 형제 하위를 가리키는 데 사용되며 간단한 연결 목록으로 구현됩니다.

노드의 child 포인터는 노드의 첫 번째 자식을 가리키는 데 사용됩니다.

다음은이 코드를 함께 사용하는 방법을 보여주는 몇 가지 샘플 코드입니다. 여기에는 오류 처리 기능이 포함되어 있지 않으며 완전한 솔루션을 제공하기위한 것은 아니지만 필요할 때 컴파일하고 디버거에서 실행하여 작동 방식을 완전히 이해할 수 있어야합니다.

트리 노드를 반복하는 방법을 보여주기 위해 열거 형 예제도 추가했습니다. 아마도 다른 순서로 결과를 내기 위해 이것으로 놀고 싶어 할 것입니다. 열거 형을 사용하는 것이 필요로하기에 너무 복잡하면 모든 노드를 방문하기 위해 간단한 재사용 메소드를 작성해야합니다.

이 예제에서는 노드 유형이 일반적이므로이 코드는 문자열 데이터를 저장하는 데만 사용됩니다. 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); 
     } 
    } 
} 

(I이 위 에릭의 대답과 비슷합니다 알고, 내가 아마 귀찮게하지 않았을이 하나를 쓰기 전에 그 답을 읽어 줄 경우 -하지만 난 이미 작성된 것입니다 내가하지 않았다 그냥 던져 버리고 싶다.)