โครงสร้างข้อมูลแบบต้นไม้ใน C #


248

ฉันกำลังมองหาโครงสร้างข้อมูลแบบต้นไม้หรือกราฟใน C # แต่ฉันคิดว่าไม่มีให้ การตรวจสอบโครงสร้างข้อมูลอย่างกว้างขวางโดยใช้ C # 2.0อธิบายถึงสาเหตุเล็กน้อย มีห้องสมุดที่สะดวกซึ่งมักใช้เพื่อให้การทำงานนี้หรือไม่ อาจผ่านรูปแบบกลยุทธ์เพื่อแก้ไขปัญหาที่นำเสนอในบทความ

ฉันรู้สึกงี่เง่าที่จะใช้ต้นไม้ของตัวเองเช่นเดียวกับที่ฉันใช้ ArrayList ของตัวเอง

ฉันต้องการต้นไม้ทั่วไปที่ไม่สมดุล คิดว่าเป็นต้นไม้ไดเรกทอรี C5 ดูดี แต่โครงสร้างต้นไม้ของพวกมันดูเหมือนจะถูกนำไปใช้เป็นต้นไม้สีแดงดำที่สมดุลที่เหมาะสมกว่าในการค้นหามากกว่าการแสดงลำดับชั้นของโหนด


2
กัดต้นไม้ที่รุนแรงมากขึ้น: stackoverflow.com/questions/196294/… ;-)
Tuomas Hietanen

มีเหตุผลบางอย่างที่ไม่สามารถรวม TreeView ในโครงการและใช้งานได้หรือไม่? ไม่มีเหตุผลที่จะแสดงให้ผู้ใช้เห็น แน่นอนว่ามีหลายรูปแบบของโครงการเมื่อไม่มีตัวเลือก หนึ่งสามารถสร้างคลาสใหม่ที่สืบทอดมาจาก TreeNode ตัวอย่างถ้าจำเป็นต้องมีความซับซ้อนพิเศษหรือไม่
เพียงแค่ G.

9
ฉันคิดว่าเป็นความคิดที่ดีที่จะนำเข้าไลบรารี UI ทั้งหมดสำหรับต้นไม้ที่ง่ายมาก
กระตุ้น

1
คุณสามารถกระตุ้น มันไม่เหมือนความต้องการพื้นที่ฮาร์ดไดรฟ์ที่แท้จริงเป็นปัญหาอีกต่อไปแล้วใช่ไหม ซุ่มซ่าม? ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้ฉันสามารถเข้าใจได้ว่านี่ไม่ใช่วิธีแก้ปัญหาสำหรับซอฟต์แวร์พิเศษหรือบางอย่างโดยไม่มีส่วนต่อประสานผู้ใช้ที่มีอยู่ ฉันเป็นโปรแกรมเมอร์ที่ขี้เกียจถ้าฉันสามารถสร้างโครงสร้างให้ฟรีมันก็ดี และห้องสมุดที่มีอยู่มีจำนวนมากฟรีหนึ่งสามารถค้นหารหัสจำนวนมากจากคนที่ใช้มันสำหรับสิ่งต่าง ๆ มากมาย
เพียงแค่ G.

1
นี่คือประเภทต้นไม้อย่างง่าย: public class Tree<T> : List<Tree<T>> { public T Value; }.
Enigmativity

คำตอบ:


155

คำแนะนำที่ดีที่สุดของฉันคือว่าไม่มีโครงสร้างข้อมูลทรีมาตรฐานเนื่องจากมีหลายวิธีที่คุณสามารถใช้งานได้ซึ่งจะเป็นไปไม่ได้ที่จะครอบคลุมฐานทั้งหมดด้วยโซลูชันเดียว ยิ่งโซลูชันมีความเฉพาะเจาะจงมากเท่าใดโอกาสที่จะเกิดปัญหาก็จะยิ่งน้อยลงเท่านั้น ฉันยังรู้สึกรำคาญกับ LinkedList - จะทำอย่างไรถ้าฉันต้องการรายการที่เชื่อมโยงแบบวงกลม

โครงสร้างพื้นฐานที่คุณจะต้องนำไปใช้จะเป็นชุดของโหนดและนี่คือตัวเลือกบางอย่างเพื่อให้คุณเริ่มต้นได้ สมมติว่าคลาสโหนดเป็นคลาสพื้นฐานของโซลูชันทั้งหมด

หากคุณต้องการเพียงนำทางลงต้นไม้แล้วชั้นโหนดต้องการรายชื่อของเด็ก

หากคุณต้องการที่จะสำรวจต้นไม้แล้วชั้นโหนดต้องการลิงค์ไปยังโหนดแม่ของมัน

สร้างวิธีการ AddChild ที่ดูแล minutia ทั้งหมดของจุดสองจุดนี้และตรรกะทางธุรกิจอื่น ๆ ที่ต้องดำเนินการ (ข้อ จำกัด ลูกการเรียงลำดับลูก ๆ เป็นต้น)


5
โดยส่วนตัวฉันจะไม่คำนึงถึงการปรับสมดุลต้นไม้ไบนารีด้วยตนเองเพื่อเพิ่มลงในห้องสมุดเนื่องจากนี่เป็นงานพิเศษมากกว่าเพียงแค่ใช้รายการ adjaceny
jk

8
@jk ฉันเชื่อว่า SortedDictionary และ SortedSet ถูกสร้างขึ้นบนต้นไม้สีแดง / สีดำดังนั้นการใช้สิ่งเหล่านี้ควรจะทำงานได้
จอน

ดูรูปแบบคอมโพสิต ;-) ว่าสิ่งที่คุณกำลังมองหา
Nicolas Voron

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<>นอกจากนี้ยังมีผู้ร่วมประชุมคล้ายคลึงสำหรับฟังก์ชั่นที่เรียกว่า
Benny Jobigan

2
ฉันจะเรียกผู้แทนนี้ได้อย่างไร
อิสระที่

3
การเปลี่ยนวิธีการสำรวจเป็นแบบคงที่หรือเป็นไปได้ที่จะซ่อนธรรมชาติแบบวนซ้ำจะเป็นความคิดที่ดี แต่มันง่ายในการสำรวจ: สร้างวิธีที่มีลายเซ็นของตัวแทนเช่นต้นไม้แห่ง int: โมฆะ my_visitor_impl (int datum) - ทำให้มันคงที่หากคุณต้องการยกตัวอย่าง delgate: 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จะใช้งานได้ในกรณีเดียวหรือไม่ มันเป็นเรื่องจริง สิ่งนี้ทำให้ฉันสับสน
Waseem Ahmad Naeem

57

นี่คือของฉันซึ่งคล้ายกับของAaron Gageซึ่งเป็นเรื่องธรรมดามากกว่าเล็กน้อยในความคิดของฉัน สำหรับวัตถุประสงค์ของฉันฉันยังไม่ได้วิ่งเข้าไปในปัญหาประสิทธิภาพการทำงานใด ๆ 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()));
        }
    }
}

เหตุใดคุณสมบัติค่าของคุณจึงถูกเปิดเผยเมื่อคุณตั้งค่าในตัวสร้าง ที่ปล่อยให้เปิดสำหรับการจัดการหลังจากที่คุณได้ตั้งค่าผ่านทางคอนสตรัค? ควรเป็นชุดส่วนตัวหรือไม่
PositiveGuy

แน่นอนว่าทำไมไม่ทำให้ไม่เปลี่ยนรูป แก้ไข
Ronnie Overby

ขอบคุณ! ฉันค่อนข้างชอบที่จะไม่ต้องเขียนเอง (ยังไม่สามารถเชื่อว่ามันไม่ได้เป็นสิ่งที่มีอยู่โดยกำเนิดผมคิดเสมอว่า .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");
    }
}

โบนัส
ดูต้นไม้ที่เต็มไปด้วย:

  • iterator
  • ค้นหา
  • Java / C #

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


ฉันจะใช้การค้นหาในตัวอย่างรหัสของคุณได้อย่างไร ในกรณีที่ไม่nodeมาจากไหน? หมายความว่าฉันต้องย้ำต้นไม้เพื่อที่จะใช้รหัสค้นหา?
BadmintonCat

@GrzegorzDev อาจเป็น -1 เพราะมันไม่ได้ใช้กับIEnumerable<>สมาชิกทั้งหมดดังนั้นจึงไม่ได้รวบรวม
Uwe Keim

1
@UweKeim Good Job ครั้งต่อไปลองใช้รหัสกับการใช้งานจริง
szab.kel

ปัญหาเดียวที่ฉันเห็นคือมันจะไม่ต่อเนื่องอย่างถูกต้องกับ JsonConvert ขั้นพื้นฐานตามที่ใช้ IEnumerable <>
Rakiah

22

ห้องสมุด C5 Generic Collection ที่ยอดเยี่ยมโดยทั่วไปมีโครงสร้างข้อมูลแบบต้นไม้หลายแบบรวมถึงชุดกระเป๋าและพจนานุกรม มีซอร์สโค้ดถ้าคุณต้องการศึกษารายละเอียดการใช้งาน (ฉันใช้คอลเลกชัน C5 ในรหัสการผลิตที่มีผลลัพธ์ที่ดีแม้ว่าฉันจะไม่ได้ใช้โครงสร้างแบบต้นไม้ใด ๆ โดยเฉพาะ)


7
ไม่ทราบว่าอาจมีการเปลี่ยนแปลงหรือไม่ แต่ตอนนี้หนังสือเล่มนี้สามารถดาวน์โหลดได้ฟรีในรูปแบบ PDF จากเว็บไซต์ C5
Oskar

4
การขาดเอกสารไม่ต้องกังวลอีกต่อไปเนื่องจากมีไฟล์ PDF ยาวถึง 272 หน้าประกอบกับห้องสมุด ... ไม่สามารถแสดงความคิดเห็นเกี่ยวกับคุณภาพของรหัส แต่ตัดสินจากคุณภาพของเอกสารฉันรอคอยที่จะขุดลึกลงไปในคืนนี้!
Florian Doyon

2
จากสิ่งที่ฉันเข้าใจห้องสมุด C5 นี้ไม่มีต้นไม้เลย แต่มีเพียงบางโครงสร้างข้อมูลที่ได้มาจากต้นไม้
roim

10

ดูhttp://quickgraph.codeplex.com/

QuickGraph นำเสนอโครงสร้างข้อมูลและอัลกอริทึมแบบกำหนดทิศทาง / ไม่ระบุทิศทางแบบทั่วไปสำหรับ. Net 2.0 ขึ้นไป QuickGraph มาพร้อมกับอัลกอริธึมเช่นการค้นหาความลึกแรกการค้นหาลมหายใจแรกการค้นหา A * เส้นทางที่สั้นที่สุดเส้นทางที่สั้นที่สุด k-shortest การไหลสูงสุดต้นไม้ที่ทอดขั้นต่ำบรรพบุรุษที่พบน้อยที่สุดเป็นต้น ... QuickGraph รองรับ MSAGL, GLEE และ Graphviz แสดงกราฟ, ทำให้เป็นอนุกรมเพื่อ GraphML, ฯลฯ ...


8

หากคุณต้องการเขียนของคุณเองคุณสามารถเริ่มต้นด้วยเอกสารหกส่วนนี้ซึ่งมีรายละเอียดการใช้งานโครงสร้างข้อมูล C # 2.0 ที่มีประสิทธิภาพและวิธีการวิเคราะห์การใช้งานโครงสร้างข้อมูลใน C # แต่ละบทความมีตัวอย่างและตัวติดตั้งพร้อมตัวอย่างที่คุณสามารถติดตามได้

“ การตรวจสอบโครงสร้างข้อมูลอย่างกว้างขวางโดยใช้ C # 2.0”โดย Scott Mitchell


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 เพื่อเริ่มต้นที่โหนดรูทจากนั้นใช้เมธอดทราเวิร์สที่เพิ่มเข้าไปใน int แต่ละการวนซ้ำ
Edward

คุณจะค้นหาต้นไม้ทั้งหมดเพื่อหาโหนดเฉพาะได้อย่างไร
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

ฉันสร้างคลาสโหนดที่สามารถเป็นประโยชน์สำหรับผู้อื่น ชั้นเรียนมีคุณสมบัติเช่น:

  • เด็ก ๆ
  • บรรพบุรุษ
  • ลูกหลาน
  • พี่น้อง
  • ระดับของโหนด
  • ผู้ปกครอง
  • ราก
  • เป็นต้น

นอกจากนี้ยังมีความเป็นไปได้ในการแปลงรายการแบบคงที่ของรายการด้วย Id และ ParentId เป็นแผนผัง โหนดเก็บการอ้างอิงกับทั้งลูกและพาเรนต์ดังนั้นการทำซ้ำโหนดจึงค่อนข้างเร็ว


2

เพราะมันไม่ได้กล่าวถึงฉันต้องการให้คุณดึงดูดความสนใจในขณะนี้. net รหัสฐาน: รหัสเฉพาะสำหรับ SortedSetที่ใช้ Red-Black-Tree:

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

อย่างไรก็ตามนี่คือโครงสร้างต้นไม้ที่สมดุล ดังนั้นคำตอบของฉันคือการอ้างอิงถึงสิ่งที่ฉันเชื่อว่าเป็นโครงสร้างต้นไม้พื้นเมืองเพียงอย่างเดียวใน. core core library


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

การออกแบบที่ดี อย่างไรก็ตามฉันไม่แน่ใจว่าโหนด 'เป็น' ลำดับของโหนดลูกหรือไม่ ฉันจะพิจารณาสิ่งต่อไปนี้: โหนด 'มี' โหนดลูกมากกว่าหรือน้อยกว่าดังนั้นโหนดจึงไม่ได้มาจากลำดับของโหนดลูก แต่เป็นการรวมกัน (เรียงองค์ประกอบ?) ของโหนดลูก
Harald Coppoolse

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

คุณสามารถใช้ initializers:

    var tree = new Tree<string>("root")
    {
        new Tree<string>("sample")
        {
            "console1"
        }
    };

1

ต้นไม้ส่วนใหญ่เกิดขึ้นจากข้อมูลที่คุณกำลังประมวลผล

สมมติว่าคุณมีpersonคลาสที่มีรายละเอียดของใครบางคน parentsคุณจะมีโครงสร้างต้นไม้เป็นส่วนหนึ่งของ "โดเมนคลาส" ของคุณหรือใช้คลาสต้นไม้แยกต่างหากที่มีลิงก์ไปยังวัตถุบุคคลของคุณหรือไม่ ลองนึกถึงการดำเนินการอย่างง่ายเช่นการรับทั้งหมดgrandchildrenของpersonรหัสนี้ควรอยู่ในperson ชั้นเรียนหรือผู้ใช้ของpersonชั้นควรรู้เกี่ยวกับต้นไม้ที่แยกจากกันหรือไม่

อีกตัวอย่างหนึ่งคือทรีการแยกวิเคราะห์ในคอมไพเลอร์ ...

ตัวอย่างทั้งสองนี้แสดงให้เห็นว่าแนวคิดของทรีเป็นส่วนหนึ่งของโดเมนของข้อมูลและการใช้ทรีวัตถุประสงค์ทั่วไปแยกจากกันอย่างน้อยสองเท่าของจำนวนวัตถุที่สร้างขึ้นรวมทั้งทำให้ API ยากขึ้นในการเขียนโปรแกรมอีกครั้ง

สิ่งที่เราต้องการคือวิธีที่จะนำการดำเนินการต้นไม้มาตรฐานกลับมาใช้ใหม่โดยไม่ต้องนำกลับมาใช้ใหม่สำหรับต้นไม้ทั้งหมดในขณะที่ในเวลาเดียวกันโดยไม่ต้องใช้ชั้นต้นไม้มาตรฐาน Boost พยายามแก้ไขปัญหาประเภทนี้สำหรับ C ++ แต่ฉันยังไม่เห็นผลใด ๆ สำหรับ. NET ที่ปรับตัว


@Puchacz ขออภัยฉันอายุ 15 ปีจากข้อมูลบน C ++ ได้ดูที่ Boost และเทมเพลตหลังจากการศึกษาที่อ่อนแอคุณอาจเข้าใจพวกเขา พลังงานมีต้นทุนการเรียนรู้สูง !!
Ian Ringrose

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

การสำรวจควรเป็นวิธีคงที่หรือไม่ มันดูน่าอึดอัดใจมากที่วิธีการอินสแตนซ์ส่งผ่านตัวมันเอง
Sinaesthetic

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 ได้โดยไม่ต้องใส่ GUI แต่มันมีค่าใช้จ่ายมากกว่าการใช้ TreeNode แบบง่ายๆ


-4

ในกรณีที่คุณต้องการการนำโครงสร้างข้อมูลทรูที่ใช้หน่วยความจำน้อยคุณสามารถเขียนคลาสโหนดของคุณดังต่อไปนี้ (การใช้ 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 # ไม่ใช่แนวคิดที่ดีที่สุด Jake โดยเฉพาะอย่างยิ่งที่มีพอยน์เตอร์ คุณรู้หรือไม่ว่าตัวชี้กำลังถูกตามล่าอย่างไร้ความปราณีใน C # ใช่ไหม? : p
ThunderGr

2
@ThunderGr ที่ไม่ยุติธรรม การตอบใน C # น่าจะดีกว่านี้ แต่ C #-วิทยากรสามารถเข้าใจพอยน์เตอร์พอยน์เตอร์เหล่านี้เป็นข้อมูลอ้างอิง หลังจาก David Boike, Aaron Gage, Ronnie Overby, Grzegorz Dev, Berezh และ Erik Nagel ต่างก็มีโครงสร้างข้อมูลเดียวกันโดยมีความแตกต่างเล็กน้อยในการแสดงออก Jake รับหน้าที่ทำลายรายการเชื่อมโยงที่ให้โครงสร้างที่เรียบง่ายเพียงโหนดเดียวและ การนำทางของพี่น้อง อย่าแสดงความไม่ชอบ C ++ ของคุณด้วยการลงคำตอบที่สร้างสรรค์
migle

3
@migle ฉันไม่ได้ลงคะแนนคำตอบ (ไม่ได้ยกระดับอย่างใดอย่างหนึ่ง) และฉันไม่ชอบ C ++ ฉันเห็นว่าคำตอบนั้นถูกลดระดับลงโดยไม่มีใครแนะนำอะไรกับ Jake เกี่ยวกับสาเหตุและวิธีที่เขาจะปรับปรุงคำตอบของเขา มันไม่เกี่ยวกับ "กำลังดี" คำถามถูกติดแท็กสำหรับ C # เท่านั้น ไม่แนะนำให้โพสต์คำตอบในภาษาอื่นนอกเหนือจากแท็กและบางคนอาจ downvote
ThunderGr
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.