ฉันจะสำรวจต้นไม้โดยไม่ใช้การเรียกซ้ำได้อย่างไร


19

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

การสำรวจเส้นทางทำงานเช่นนี้

private Data Execute(Node pNode)
{
    Data[] values = new Data[pNode.Children.Count];
    for(int i=0; i < pNode.Children.Count; i++)
    {
        values[i] = Execute(pNode.Children[i]);  // recursive
    }
    return pNode.Process(values);
}

public void Start(Node pRoot)
{
    Data result = Execute(pRoot);
}

ใช้งานได้ดี แต่ฉันกังวลว่า call call จะ จำกัด ขนาดของทรีโหนด

วิธีการสามารถรหัสถูกเขียนใหม่เพื่อให้ไม่มีการโทร recursive จะExecuteทำ?


8
คุณจะต้องรักษาสแต็คของคุณเองเพื่อติดตามโหนดหรือเปลี่ยนรูปร่างของต้นไม้ ดูstackoverflow.com/q/5496464และstackoverflow.com/q/4581576
Robert Harvey

1
ฉันยังพบมากของความช่วยเหลือที่นี้การค้นหาของ Googleโดยเฉพาะมอร์ริสการข้ามผ่าน
Robert Harvey

@ RobertHarvey ขอบคุณ Rob ฉันไม่แน่ใจว่าเงื่อนไขนี้จะเป็นอย่างไร
เปิดใช้งานใหม่

2
คุณอาจแปลกใจที่ความต้องการของหน่วยความจำถ้าคุณทำคณิตศาสตร์ ตัวอย่างเช่นไบนารีเทอราโนดที่สมดุลอย่างสมบูรณ์ต้องการเพียงสแต็ก 40 รายการที่ลึก
Karl Bielefeldt

@KarlBielefeldt ที่ถือว่าต้นไม้มีความสมดุลอย่างสมบูรณ์แม้ว่า บางครั้งคุณต้องสร้างแบบจำลองต้นไม้ที่ไม่สมดุลและในกรณีนี้มันง่ายมากที่จะระเบิดสแต็ก
Servy

คำตอบ:


27

นี่คือการใช้การสำรวจเส้นทางแบบทรีเพื่อวัตถุประสงค์ทั่วไปที่ไม่ใช้การเรียกซ้ำ

public static IEnumerable<T> Traverse<T>(T item, Func<T, IEnumerable<T>> childSelector)
{
    var stack = new Stack<T>();
    stack.Push(item);
    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childSelector(next))
            stack.Push(child);
    }
}

ในกรณีของคุณคุณสามารถเรียกได้ว่า:

IEnumerable<Node> allNodes = Traverse(pRoot, node => node.Children);

ใช้การQueueค้นหาStackแบบกลมๆก่อนแทนที่จะค้นหาความลึกก่อน ใช้PriorityQueueสำหรับการค้นหาครั้งแรกที่ดีที่สุด


ฉันคิดถูกไหมว่านี่จะทำให้ต้นไม้เรียบเป็นของสะสม?
ซ้ำ

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

ฉันไม่คิดว่าจะแก้ปัญหาได้
ซ้ำ

4
เขาไม่เพียง แต่สำรวจกราฟที่มีการทำงานอิสระเช่นการค้นหาเท่านั้นเขายังรวบรวมข้อมูลจากโหนดลูก การทำให้ต้นไม้เรียบจะทำลายข้อมูลโครงสร้างที่ต้องการเพื่อทำการรวม
Karl Bielefeldt

1
FYI ฉันคิดว่านี่เป็นคำตอบที่ถูกต้องที่คนส่วนใหญ่ googling คำถามนี้กำลังมองหา +1
Anders Arpi

4

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

http://www.atalasoft.com/cs/blogs/rickm/archive/2008/04/22/increasing-the-size-of-your-stack-net-memory-management-part-3.aspx

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


ฉันเพิ่งทำแบบทดสอบอย่างรวดเร็ว บนเครื่องของฉันฉันสามารถโทรซ้ำได้ถึง 14,000 ครั้งก่อนจะถึง stackoverflow หากทรีมีความสมดุลเพียง 32 สายจำเป็นต้องมีการจัดเก็บ 4 พันล้านโหนด หากแต่ละโหนดมี 1 ไบต์ (ซึ่งไม่เป็นเช่นนั้น) จะต้องใช้ RAM 4 GB ในการจัดเก็บต้นไม้ที่มีความสมดุลสูง 32
Esben Skov Pedersen

ฉันเราต้องใช้การโทรทั้งหมด 14,000 ครั้งในสแต็ก ต้นไม้จะใช้เวลา 2.6x10 ^ 4214 ไบต์หากแต่ละโหนดมีหนึ่งไบต์ (ซึ่งไม่เป็นเช่นนั้น)
Esben Skov Pedersen

-3

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

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


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

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

คุณจะกำหนดการสอบถามซ้ำได้อย่างไร? ฉันจะนิยามมันเป็นฟังก์ชั่นที่เรียกใช้ตัวเองภายในคำจำกัดความของตัวเอง แน่นอนที่สุดคุณสามารถสำรวจต้นไม้โดยไม่ต้องทำอย่างที่ฉันได้แสดงให้เห็นในคำตอบของฉัน
Servy

2
ฉันเป็นคนชั่วหรือเปล่าที่ได้เพลิดเพลินกับการคลิกลงคะแนนกับคนที่มีคะแนนสูงเช่นนี้? มันเป็นความสุขที่หายากบนเว็บไซต์นี้
Reactgular

2
มาที่ @ Mat นั่นมันเรื่องเด็ก คุณอาจไม่เห็นด้วยเช่นถ้าคุณกลัวที่จะทิ้งระเบิดบนต้นไม้ที่ลึกเกินไปนั่นเป็นข้อกังวลที่สมเหตุสมผล คุณสามารถพูดได้
Mike Dunlavey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.