ความกว้างแรกและความลึกก่อน


172

เมื่อทำการสำรวจต้นไม้ / กราฟความแตกต่างระหว่างความกว้างก่อนและระยะลึกก่อนคืออะไร ตัวอย่างการเข้ารหัสหรือรหัสเทียมใด ๆ จะดีมาก


6
คุณตรวจสอบวิกิพีเดีย ( ความลึกครั้งแรก , ความกว้างเป็นครั้งแรก ) มีตัวอย่างโค้ดในหน้าเหล่านั้นพร้อมด้วยรูปภาพสวย ๆ มากมาย
rmeador

ฉันมีความคิดเช่นนั้น แต่จากตัวอย่างที่ให้มานั้นค่อนข้างดีกว่าที่พบในวิกิพีเดีย ....
jonnybazookatone

คำตอบ:


292

คำสองคำนี้แยกความแตกต่างระหว่างการเดินต้นไม้สองวิธี

มันอาจจะง่ายที่สุดเพียงแค่แสดงความแตกต่าง พิจารณาต้นไม้:

    A
   / \
  B   C
 /   / \
D   E   F

การสำรวจเส้นทางแรกที่มีความลึกจะเยี่ยมชมโหนดตามลำดับนี้

A, B, D, C, E, F

สังเกตว่าคุณลงไปถึงขาข้างหนึ่งก่อนที่จะเดินต่อไป

การสำรวจเส้นทางแรกที่กว้างที่สุดจะไปที่โหนดตามลำดับนี้

A, B, C, D, E, F

ที่นี่เราทำงานไปทั่วทุกระดับก่อนลงไป

(โปรดสังเกตว่ามีความคลุมเครือบางอย่างในคำสั่งการแวะผ่านและฉันก็โกงที่จะรักษาลำดับ "การอ่าน" ในแต่ละระดับของต้นไม้ในทั้งสองกรณีฉันสามารถไปที่ B ก่อนหรือหลัง C และในทำนองเดียวกันฉันก็สามารถ E ก่อนหรือหลัง F. สิ่งนี้อาจจะใช่หรือไม่ใช่ก็ได้ขึ้นอยู่กับการสมัครของคุณ ... )


การสำรวจเส้นทางทั้งสองประเภทนี้สามารถทำได้ด้วยการปลอมแปลง:

Store the root node in Container
While (there are nodes in Container)
   N = Get the "next" node from Container
   Store all the children of N in Container
   Do some work on N

Containerความแตกต่างระหว่างสองคำสั่งสำรวจเส้นทางการโกหกในการเลือกของ

  • สำหรับความลึกอันดับแรกให้ใช้สแต็ก (การใช้งานแบบเรียกซ้ำใช้ call-stack ... )
  • สำหรับความกว้างใช้คิว

การใช้งานแบบเรียกซ้ำดูเหมือนว่า

ProcessNode(Node)
   Work on the payload Node
   Foreach child of Node
      ProcessNode(child)
   /* Alternate time to work on the payload Node (see below) */

การเรียกซ้ำจะสิ้นสุดลงเมื่อคุณไปถึงโหนดที่ไม่มีลูกดังนั้นจึงรับประกันได้ว่าจะสิ้นสุดสำหรับกราฟ จำกัด


เมื่อมาถึงจุดนี้ฉันยังคงโกงเล็กน้อย ด้วยความฉลาดเล็กน้อยคุณสามารถทำงานกับโหนดตามลำดับนี้ได้:

D, B, E, F, C, A

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

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


@dmckee ขอบคุณ! ฉันเชื่อว่าคุณหมายถึง "ทำงานกับน้ำหนักบรรทุกที่โหนด" ใช่ไหม?
batbrat

4
คุณควรแก้ไขเวอร์ชันความลึกแรกเพื่อรับส่วนนำหน้า ( A, B, D, C, E, F- อันแรกที่นำเสนอ), infix ( D, B, A, E, C, F- ใช้สำหรับการเรียงลำดับ: เพิ่มเป็นแผนผัง AVL จากนั้นอ่าน infix) หรือ postfix ( D, B, E, F, C, Aทางเลือกที่นำเสนอ) ชื่อถูกกำหนดโดยตำแหน่งที่คุณประมวลผลรูท ควรสังเกตว่ามัดเท่านั้นทำให้รู้สึกสำหรับต้นไม้ไบนารี @batbrat เป็นชื่อ ... ได้รับเวลาตั้งแต่ที่คุณถามคุณอาจรู้อยู่แล้ว
ธีรโรจน์

@Theraot ขอบคุณที่เพิ่มใน! ใช่ฉันรู้เกี่ยวกับการสำรวจเส้นทางเหล่านี้และทำไม Infix จึงสมเหตุสมผลสำหรับต้นไม้ไบนารี
batbrat

จะตัดสินใจได้อย่างไรว่าโซลูชันใดมีพื้นที่หรือเวลาซับซ้อนกว่ากัน
IgorGanapolsky

1
@IgorGanapolsky ควรเหมือนกันสำหรับทั้งคู่ในหลักการ (หลังจากพวกเขาใช้รหัสเดียวกันเป็นหลัก) คำถามที่น่าสนใจมากขึ้นก็คือวิธีที่พวกเขาส่งผลกระทบต่อแคชและชุดการทำงาน แต่ฉันคิดว่าจะขึ้นอยู่กับลักษณะทางสัณฐานวิทยาของต้นไม้
dmckee --- ผู้ดูแลอดีตลูกแมว

95

ทำความเข้าใจกับข้อกำหนด:

ภาพนี้ควรให้ความคิดเกี่ยวกับบริบทที่คำว่าความกว้างและความลึกจะใช้

ทำความเข้าใจความกว้างและความลึก


ค้นหาความลึกครั้งแรก:

การค้นหาความลึกครั้งแรก

  • อัลกอริทึมการค้นหาความลึกแรกทำหน้าที่ราวกับว่ามันต้องการได้ไกลจากจุดเริ่มต้นโดยเร็วที่สุด

  • โดยทั่วไปใช้ a Stackเพื่อจดจำว่าควรไปที่ไหนเมื่อถึงจุดจบ

  • กฎที่จะปฏิบัติตาม: กดจุดสุดยอด A แรกไปที่ Stack

    1. ถ้าเป็นไปได้ให้ไปที่จุดสุดยอดที่ไม่เคยเยี่ยมชมติดกันทำเครื่องหมายว่าเข้าชมแล้วกดลงบนสแต็ก
    2. หากคุณไม่สามารถติดตามกฎ 1 ได้ถ้าเป็นไปได้ให้วางจุดยอดออกจากสแต็ก
    3. หากคุณไม่สามารถติดตามกฎ 1 หรือกฎ 2 ได้แสดงว่าคุณทำเสร็จแล้ว
  • รหัส Java:

    public void searchDepthFirst() {
        // Begin at vertex 0 (A)
        vertexList[0].wasVisited = true;
        displayVertex(0);
        stack.push(0);
        while (!stack.isEmpty()) {
            int adjacentVertex = getAdjacentUnvisitedVertex(stack.peek());
            // If no such vertex
            if (adjacentVertex == -1) {
                stack.pop();
            } else {
                vertexList[adjacentVertex].wasVisited = true;
                // Do something
                stack.push(adjacentVertex);
            }
        }
        // Stack is empty, so we're done, reset flags
        for (int j = 0; j < nVerts; j++)
            vertexList[j].wasVisited = false;
    }
    
  • แอปพลิเคชัน : การค้นหาความลึกครั้งแรกมักใช้ในการจำลองเกม (และสถานการณ์ที่เหมือนเกมในโลกแห่งความเป็นจริง) ในเกมทั่วไปคุณสามารถเลือกหนึ่งในการกระทำที่เป็นไปได้หลายอย่าง แต่ละตัวเลือกนำไปสู่การเลือกเพิ่มเติมซึ่งแต่ละทางเลือกนำไปสู่การเลือกเพิ่มเติมและอื่น ๆ ลงในกราฟของความเป็นไปได้รูปต้นไม้


ค้นหาความกว้าง - แรก:

ค้นหาความกว้าง - แรก

  • อัลกอริทึมการค้นหาแบบกว้างแรกชอบให้อยู่ใกล้จุดเริ่มต้นมากที่สุด
  • Queueชนิดของการค้นหานี้จะดำเนินการโดยทั่วไปใช้
  • กฎที่ควรปฏิบัติ: ทำให้การเริ่มต้นจุดยอด A จุดสุดยอดปัจจุบัน
    1. เยี่ยมชมจุดสุดยอดที่ไม่ได้เยี่ยมชมถัดไป (ถ้ามี) ที่อยู่ติดกับจุดสุดยอดปัจจุบันทำเครื่องหมายและแทรกลงในคิว
    2. หากคุณไม่สามารถดำเนินการตามกฎข้อ 1 ได้เนื่องจากไม่มีจุดยอดที่ไม่ได้เข้ามาอีกให้ลบจุดสุดยอดออกจากคิว (ถ้าเป็นไปได้) และทำให้เป็นจุดยอดปัจจุบัน
    3. หากคุณไม่สามารถดำเนินการตามกฎข้อ 2 ได้เนื่องจากคิวว่างเปล่า
  • รหัส Java:

    public void searchBreadthFirst() {
        vertexList[0].wasVisited = true;
        displayVertex(0);
        queue.insert(0);
        int v2;
        while (!queue.isEmpty()) {
            int v1 = queue.remove();
            // Until it has no unvisited neighbors, get one
            while ((v2 = getAdjUnvisitedVertex(v1)) != -1) {
                vertexList[v2].wasVisited = true;
                // Do something
                queue.insert(v2);
            }
        }
        // Queue is empty, so we're done, reset flags
        for (int j = 0; j < nVerts; j++) 
            vertexList[j].wasVisited = false;
    }
    
  • แอปพลิเคชั่น : การค้นหาความกว้างก่อนอื่นก่อนพบจุดยอดทั้งหมดที่อยู่ห่างจากจุดเริ่มต้นหนึ่งจุดจากนั้นจุดยอดทั้งหมดที่อยู่ห่างออกไปสองขอบและอื่น ๆ สิ่งนี้มีประโยชน์หากคุณกำลังพยายามหาเส้นทางที่สั้นที่สุดจากจุดเริ่มต้นไปยังจุดสุดยอดที่กำหนด

หวังว่าควรจะเพียงพอสำหรับการทำความเข้าใจการค้นหาความกว้างและความลึกก่อน สำหรับการอ่านเพิ่มเติมฉันอยากจะแนะนำบทกราฟจากหนังสือโครงสร้างข้อมูลที่ยอดเยี่ยมโดย Robert Lafore


6
ถ้าฉันมีสิทธิออกเสียงเพิ่มอีกสิบสิทธิ์ฉันจะทำเช่นนั้น
snr

@snr คุณสามารถมอบรางวัล;)
สโนว์

@Snow ถ้าคุณบอกคำสั่งของฉันฉันสามารถ ฉันไม่รู้จะทำยังไง
snr

ขอบคุณ @snr ฉันดีใจที่ได้รับเงินรางวัลแรก ฉันซาบซึ้งมาก
Yogesh Umesh Vaity

1
ขอบคุณ @Snow ฉันดีใจที่พวกคุณพบว่าคำตอบของฉันมีประโยชน์
Yogesh Umesh Vaity

4

รับต้นไม้ไบนารีนี้:

ป้อนคำอธิบายรูปภาพที่นี่

การสำรวจเส้นทางแรกกว้าง:
ข้ามผ่านแต่ละระดับจากซ้ายไปขวา

"ฉัน G ลูก ๆ ของฉันคือ D และฉันหลานของฉันคือ B, E, H และ K หลานของพวกเขาคือ A, C, F"

- Level 1: G 
- Level 2: D, I 
- Level 3: B, E, H, K 
- Level 4: A, C, F

Order Searched: G, D, I, B, E, H, K, A, C, F

การสำรวจเส้นทางลึกครั้งแรก: การ
สำรวจเส้นทางไม่ได้ทำผ่านข้ามระดับทั้งหมดในเวลาเดียวกัน แต่กลับข้ามไปดำดิ่งเข้าไปในความลึก (จากรากถึงใบไม้) ของต้นไม้ก่อน อย่างไรก็ตามมันค่อนข้างซับซ้อนกว่าเพียงแค่ขึ้นและลง

มีสามวิธี:

1) PREORDER: ROOT, LEFT, RIGHT.
You need to think of this as a recursive process:  
Grab the Root. (G)  
Then Check the Left. (It's a tree)  
Grab the Root of the Left. (D)  
Then Check the Left of D. (It's a tree)  
Grab the Root of the Left (B)  
Then Check the Left of B. (A)  
Check the Right of B. (C, and it's a leaf node. Finish B tree. Continue D tree)  
Check the Right of D. (It's a tree)  
Grab the Root. (E)  
Check the Left of E. (Nothing)  
Check the Right of E. (F, Finish D Tree. Move back to G Tree)  
Check the Right of G. (It's a tree)  
Grab the Root of I Tree. (I)  
Check the Left. (H, it's a leaf.)  
Check the Right. (K, it's a leaf. Finish G tree)  
DONE: G, D, B, A, C, E, F, I, H, K  

2) INORDER: LEFT, ROOT, RIGHT
Where the root is "in" or between the left and right child node.  
Check the Left of the G Tree. (It's a D Tree)  
Check the Left of the D Tree. (It's a B Tree)  
Check the Left of the B Tree. (A)  
Check the Root of the B Tree (B)  
Check the Right of the B Tree (C, finished B Tree!)  
Check the Right of the D Tree (It's a E Tree)  
Check the Left of the E Tree. (Nothing)  
Check the Right of the E Tree. (F, it's a leaf. Finish E Tree. Finish D Tree)...  
Onwards until...   
DONE: A, B, C, D, E, F, G, H, I, K  

3) POSTORDER: 
LEFT, RIGHT, ROOT  
DONE: A, C, B, F, E, D, H, K, I, G

การใช้งาน (aka ทำไมเราถึงสนใจ):
ฉันชอบคำอธิบาย Quora อย่างง่าย ๆ ของวิธีการสำรวจเส้นทางลึกครั้งแรกและวิธีการใช้งานทั่วไป:
"การสำรวจเส้นทางในการสั่งซื้อจะพิมพ์ค่า [เพื่อ BST (ต้นไม้ค้นหาแบบไบนารี)] "
" การสำรวจเส้นทางการสั่งซื้อล่วงหน้าใช้เพื่อสร้างสำเนาของ [แผนผังการค้นหาแบบทวิภาค] "
"การสำรวจเส้นทางของ Postorder ใช้เพื่อลบ [แผนผังการค้นหาแบบทวิภาค]"
https://www.quora.com/What-is-the-use-of-pre-order-and-post-order-traversal-of-binary-trees-in-computing


2

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

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

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

ฉันไม่เห็นการตีความที่เป็นธรรมชาติของ DFS (เฉพาะมาตรฐานเกี่ยวกับเขาวงกต แต่มันไม่ทรงพลังเท่ากับ BFS one และน้ำท่วม) ดังนั้นสำหรับฉันดูเหมือนว่า BFS จะสัมพันธ์กับปรากฏการณ์ทางกายภาพได้ดีกว่าที่อธิบายไว้ข้างต้นในขณะที่ DFS มีความสัมพันธ์ที่ดีขึ้นกับตัวเลือก dillema บนระบบที่มีเหตุผล (เช่นคนหรือคอมพิวเตอร์ตัดสินใจว่าจะย้ายเกมหมากรุกหรือออกไปจากเขาวงกต)

ดังนั้นสำหรับฉันความแตกต่างระหว่างการโกหกซึ่งปรากฏการณ์ทางธรรมชาติที่ดีที่สุดตรงกับรูปแบบการแพร่กระจายของพวกเขา (transversing) ในชีวิตจริง


1
คุณสามารถใช้มันด้วยอัลกอริทึมที่คล้ายกันเพียงใช้ stack สำหรับ DFS และคิวสำหรับ BFS ปัญหาเกี่ยวกับ BFS คือคุณต้องติดตามทุกโหนดที่มองเห็น DFS ในสาขาฟิสิกส์ .. ฉันจินตนาการถึงจักรวาลที่เป็นทางเลือกและคุณต้องการสิ่งหนึ่งกับชีวิตลูกทุกคนที่มีรากเป็นคนใหญ่ที่แตกต่างกันและคุณไปตลอดทางจนถึงความตายของจักรวาลไม่มีชีวิต? คุณกลับไปที่การแยกไปสองทางครั้งสุดท้ายและลองอีกรอบจนกว่าจะหมดและคุณจะไปที่บิ๊กแบงถัดไปโดยตั้งกฎทางกายภาพใหม่สำหรับจักรวาลใหม่ ใช้งานง่ายสุด ๆ ปัญหาที่ดีคือการหาทางกับม้าในกระดานหมากรุก
juanmf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.