ทำไม DFS และไม่ใช่ BFS ในการค้นหาวัฏจักรในกราฟ


86

DFS ส่วนใหญ่ใช้เพื่อค้นหาวัฏจักรในกราฟไม่ใช่ BFS เหตุผลใด? ทั้งสองสามารถค้นหาได้ว่ามีการเยี่ยมชมโหนดแล้วหรือไม่ในขณะที่ข้ามต้นไม้ / กราฟ


5
ในกราฟกำกับเฉพาะ DFS เท่านั้นที่สามารถใช้เพื่อตรวจจับวัฏจักร แต่ในกราฟที่ไม่ได้บอกทิศทางสามารถใช้ได้ทั้งสองแบบ
Hengameh

คำตอบ:


74

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

นอกจากนี้หากกราฟของคุณถูกนำทางคุณจะต้องไม่เพียง แต่จำไว้ว่าคุณเคยไปที่โหนดหรือไม่ แต่ยังรวมถึงวิธีที่คุณไปที่นั่นด้วย มิฉะนั้นคุณอาจคิดว่าคุณได้พบกับวัฏจักร แต่ในความเป็นจริงสิ่งที่คุณมีคือสองเส้นทางที่แยกจากกัน A-> B แต่นั่นไม่ได้หมายความว่ามีเส้นทาง B-> A ตัวอย่างเช่น,

หากคุณทำ BFS โดยเริ่มจาก0จะตรวจพบว่ามีวงจรอยู่ แต่จริงๆแล้วไม่มีวงจร

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

สำหรับขั้นตอนวิธีการที่ดีที่สุดสำหรับการตรวจสอบรอบในกราฟที่คุณสามารถดูขั้นตอนวิธีการ Tarjan ของ


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

3
IMO จะง่ายกว่าก็ต่อเมื่อคุณสามารถพึ่งพาการเรียกซ้ำหางได้
Hank Gay

2
"ยกเลิกการทำเครื่องหมายเมื่อคุณย้อนรอย" - ด้วยความเสี่ยงของคุณเอง! สิ่งนี้สามารถนำไปสู่พฤติกรรม O (n ^ 2) ได้อย่างง่ายดายโดยเฉพาะ DFS ดังกล่าวจะเข้าใจผิดว่าขอบข้ามเป็นขอบ "ต้นไม้" (ขอบ "ต้นไม้" อาจเป็นชื่อที่ผิดเนื่องจากจะไม่สร้างต้นไม้อีกต่อไป)
Dimitris Andreou

1
@Dimitris Andreo: คุณสามารถใช้สถานะที่เยี่ยมชมสามสถานะแทนที่จะเป็นสองสถานะเพื่อปรับปรุงประสิทธิภาพ ด้วยกราฟกำกับมีความแตกต่างระหว่าง 'ฉันเคยเห็นโหนดนี้มาก่อน' และ 'โหนดนี้เป็นส่วนหนึ่งของลูป' ด้วยกราฟที่ไม่มีทิศทางจะเทียบเท่ากัน
Mark Byers

แน่นอนคุณต้องมีสถานะที่สาม (เพื่อทำให้อัลกอริทึมเป็นเส้นตรง) ดังนั้นคุณควรพิจารณาแก้ไขส่วนนั้น
Dimitris Andreou

28
  1. DFS ใช้งานได้ง่ายกว่า
  2. เมื่อ DFS พบวัฏจักรแล้วสแต็กจะมีโหนดที่สร้างวงจร เช่นเดียวกันกับ BFS ดังนั้นคุณต้องทำงานพิเศษหากคุณต้องการพิมพ์รอบที่พบด้วย ทำให้ DFS สะดวกมากขึ้น

11

BFS อาจสมเหตุสมผลหากไม่มีการกำหนดทิศทางของกราฟ (โปรดเป็นแขกของฉันในการแสดงอัลกอริทึมที่มีประสิทธิภาพโดยใช้ BFS ที่จะรายงานรอบในกราฟกำกับ!) โดยที่ "cross edge" แต่ละอันจะกำหนดวัฏจักร ถ้าครอสเอดจ์คือ{v1, v2}และรูท (ในทรี BFS) ที่มีโหนดเหล่านั้นอยู่rดังนั้นวัฏจักรคือr ~ v1 - v2 ~ r( ~คือพา ธ-ขอบเดียว) ซึ่งสามารถรายงานได้เกือบจะง่ายเหมือนใน DFS

เหตุผลเดียวที่จะใช้ BFS คือถ้าคุณรู้ว่ากราฟ (ไม่ได้บอกทิศทาง) ของคุณจะมีเส้นทางยาวและมีเส้นทางเล็ก ๆ (กล่าวอีกนัยหนึ่งคือลึกและแคบ) ในกรณีนั้น BFS จะต้องใช้หน่วยความจำสำหรับคิวน้อยกว่าตามสัดส่วนของ DFS 'stack (แน่นอนว่าทั้งคู่ยังคงเป็นเส้นตรง)

ในกรณีอื่น ๆ DFS เป็นผู้ชนะอย่างชัดเจน มันทำงานบนกราฟทั้งแบบกำหนดทิศทางและแบบไม่บอกทิศทางและเป็นเรื่องเล็กน้อยที่จะรายงานวัฏจักร - เพียงต่อขอบด้านหลังใด ๆ เข้ากับเส้นทางจากบรรพบุรุษไปยังลูกหลานและคุณจะได้รับวงจร สรุปแล้วดีกว่าและใช้งานได้จริงกว่า BFS สำหรับปัญหานี้


4

BFS จะไม่ทำงานสำหรับกราฟที่กำหนดทิศทางในการค้นหารอบ พิจารณา A-> B และ A-> C-> B เป็นเส้นทางจาก A ถึง B ในกราฟ BFS จะบอกว่าหลังจากเดินไปตามหนึ่งในเส้นทางที่ B ไปเยี่ยม เมื่อเดินทางต่อไปในเส้นทางถัดไปจะบอกว่ามีการพบโหนด B อีกครั้งดังนั้นจึงมีวงจรอยู่ที่นั่น เห็นได้ชัดว่าไม่มีวงจรที่นี่


คุณช่วยอธิบายได้ไหมว่า DFS จะระบุได้อย่างไรว่าวงจรนั้นไม่มีอยู่ในตัวอย่างของคุณฉันยอมรับว่าวงจรไม่มีอยู่ในตัวอย่างที่ให้ไว้ แต่ถ้าเราไปจาก A-> B แล้ว A-> C-> B เราจะพบ ว่า B ถูกเยี่ยมชมแล้วและพาเรนต์คือ A ไม่ใช่ C และฉันอ่านว่า DFS จะตรวจจับวัฏจักรโดยการเปรียบเทียบพาเรนต์ขององค์ประกอบที่เยี่ยมชมแล้วกับโหนดปัจจุบันจากทิศทางที่เรากำลังตรวจสอบในขณะนี้ฉันได้รับ DFS ผิดหรือ อะไร?
ตีอย่างแรง

สิ่งที่คุณแสดงไว้ที่นี่ก็คือการใช้งานเฉพาะนี้ไม่ได้ผลไม่ใช่ว่าเป็นไปไม่ได้กับ BFS ในความเป็นจริงเป็นไปได้แม้ว่าจะใช้พื้นที่ทำงานและพื้นที่มากกว่า
พรุน

@Prune: หัวข้อทั้งหมด (ฉันคิดว่า) ที่นี่กำลังพยายามพิสูจน์ว่า bfs ใช้ไม่ได้กับการตรวจจับรอบ หากคุณรู้วิธีโต้แย้งการพิสูจน์คุณควรให้การพิสูจน์ พูดง่ายๆว่าความพยายามจะไม่เพียงพอ
Aditya Raman

เนื่องจากอัลกอริทึมมีให้ในการโพสต์ที่มีการเชื่อมโยงฉันจึงไม่เหมาะสมที่จะทำโครงร่างซ้ำที่นี่
พรุน

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

4

ฉันไม่รู้ว่าทำไมคำถามเก่า ๆ ถึงโผล่ขึ้นมาในฟีดของฉัน แต่คำตอบก่อนหน้านี้ทั้งหมดไม่ดีดังนั้น ...

DFS ใช้เพื่อค้นหารอบในกราฟกำกับเพราะมันใช้งานได้

ใน DFS ทุกจุดยอดคือ "เยี่ยม" ซึ่งการเยี่ยมชมจุดยอดหมายถึง:

  1. จุดยอดเริ่มต้น
  2. มีการเยี่ยมชมย่อหน้าย่อยที่เข้าถึงได้จากจุดยอดนั้น ซึ่งรวมถึงการติดตามขอบที่ไม่มีการติดตามทั้งหมดที่สามารถเข้าถึงได้จากจุดยอดนั้นและการเยี่ยมชมจุดยอดที่ไม่สามารถเข้าถึงได้ทั้งหมด

  3. จุดยอดเสร็จสิ้น

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

เนื่องจากคุณสมบัตินี้เรารู้ว่าเมื่อจุดยอดแรกในวงจรเริ่มต้น:

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

ดังนั้นหากมีวัฏจักรเรารับประกันว่าจะพบขอบของจุดยอดเริ่มต้น แต่ยังไม่เสร็จ (2) และหากเราพบขอบดังกล่าวเราจะรับประกันได้ว่ามีวัฏจักร (3)

นั่นเป็นเหตุผลที่ DFS ใช้เพื่อค้นหารอบในกราฟกำกับ

BFS ไม่มีการรับประกันดังกล่าวดังนั้นจึงไม่ได้ผล (แม้ว่าอัลกอริธึมการค้นหาวงจรที่ดีอย่างสมบูรณ์แบบซึ่งรวม BFS หรือคล้ายกันเป็นโพรซีเดอร์ย่อย)

ในทางกลับกันกราฟที่ไม่ได้บอกทิศทางมีวัฏจักรเมื่อใดก็ตามที่มีสองเส้นทางระหว่างจุดยอดคู่ใด ๆ กล่าวคือเมื่อมันไม่ใช่ต้นไม้ สิ่งนี้ง่ายต่อการตรวจจับระหว่าง BFS หรือ DFS - ขอบที่โยงไปถึงจุดยอดใหม่จะก่อตัวเป็นต้นไม้และขอบอื่น ๆ บ่งบอกถึงวัฏจักร


อันที่จริงนี่เป็นคำตอบที่เกี่ยวข้องมากที่สุด (อาจเป็นเพียงคำตอบเดียว) ที่นี่โดยอธิบายถึงเหตุผลที่แท้จริง
plasmacel

2

หากคุณวางวงรอบไว้ที่จุดสุ่มบนต้นไม้ DFS จะมีแนวโน้มที่จะชนวงจรเมื่อมันปกคลุมต้นไม้ประมาณครึ่งหนึ่งและครึ่งหนึ่งของเวลาที่มันจะเคลื่อนที่ไปตามที่ที่วัฏจักรไปแล้วและครึ่งหนึ่งของเวลาจะไม่ ( และจะพบโดยเฉลี่ยในครึ่งหนึ่งของต้นไม้ที่เหลือ) ดังนั้นจะประเมินโดยเฉลี่ยประมาณ 0.5 * 0.5 + 0.5 * 0.75 = 0.625 ของต้นไม้

หากคุณวางวงรอบไว้ที่จุดสุ่มในต้นไม้ BFS จะมีแนวโน้มที่จะเข้าสู่วงจรก็ต่อเมื่อมีการประเมินเลเยอร์ของต้นไม้ที่ความลึกนั้น ดังนั้นคุณมักจะต้องประเมินใบไม้ของต้นไม้ไบนารีที่สมดุลซึ่งโดยทั่วไปจะส่งผลให้มีการประเมินต้นไม้มากขึ้น โดยเฉพาะอย่างยิ่ง 3/4 ของเวลาอย่างน้อยหนึ่งในสองลิงก์จะปรากฏในใบไม้ของต้นไม้และในกรณีเหล่านั้นคุณต้องประเมินโดยเฉลี่ย 3/4 ของต้นไม้ (หากมีลิงก์เดียว) หรือ 7 / 8 ของต้นไม้ (ถ้ามีสองอัน) ดังนั้นคุณถึงคาดหวังที่จะค้นหา 1/2 * 3/4 ​​+ 1/4 * 7/8 = (7 + 12) / 32 = 21/32 = 0.656 ... ของต้นไม้โดยไม่ต้องเพิ่มค่าใช้จ่ายในการค้นหาต้นไม้ด้วยวัฏจักรที่เพิ่มออกไปจากโหนดใบไม้

นอกจากนี้ DFS ยังใช้งานได้ง่ายกว่า BFS ดังนั้นจึงเป็นสิ่งที่จะใช้เว้นแต่คุณจะรู้บางอย่างเกี่ยวกับวัฏจักรของคุณ (เช่นวัฏจักรมีแนวโน้มที่จะอยู่ใกล้รูทที่คุณค้นหาซึ่งจุดนั้น BFS จะให้ข้อได้เปรียบแก่คุณ)


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

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

ตัวเลขมีมนต์ขลังเพราะไม่มีความหมายอะไรเลย คุณใช้กรณีที่ DFS ทำได้ดีกว่าและคาดคะเนผลลัพธ์เหล่านั้นกับกรณีทั่วไป ข้อความของคุณไม่มีมูล: "DFS มีแนวโน้มที่จะเข้าสู่วงจรเมื่อมันครอบคลุมต้นไม้ครึ่งหนึ่ง": พิสูจน์ได้ ไม่ต้องพูดถึงว่าคุณไม่สามารถพูดถึงวัฏจักรในต้นไม้ได้ ต้นไม้ไม่มีวัฏจักรตามความหมาย ฉันไม่เห็นว่าประเด็นของคุณคืออะไร DFS จะไปทางเดียวจนกว่าจะถึงทางตันดังนั้นคุณไม่มีทางรู้ได้เลยว่ากราฟ (NOT tree) จะสำรวจโดยเฉลี่ยเท่าใด คุณเพียงแค่เลือกกรณีสุ่มที่พิสูจน์ว่าไม่มีอะไร
IVlad

@Vlad - กราฟที่ไม่ได้กำหนดทิศทางที่เชื่อมต่อแบบ noncyclic ทั้งหมดเป็นต้นไม้ (ไม่มีการรูทไม่ได้กำหนดทิศทาง) ฉันหมายถึง "กราฟที่จะเป็นต้นไม้บันทึกลิงก์ปลอม" บางทีนี่อาจไม่ใช่แอปพลิเคชันหลักสำหรับอัลกอริทึม - บางทีคุณอาจต้องการค้นหารอบในกราฟที่พันกันซึ่งมีลิงก์จำนวนมากที่ทำให้มันไม่ใช่ต้นไม้ แต่ถ้าเป็นแบบต้นไม้โดยเฉลี่ยในกราฟทั้งหมดโหนดใด ๆ ก็มีแนวโน้มที่จะเป็นแหล่งที่มาของลิงก์ปลอมดังกล่าวได้เท่า ๆ กันซึ่งจะทำให้ครอบคลุมต้นไม้ที่คาดไว้ 50% เมื่อลิงก์ถูกกระทบ ดังนั้นฉันยอมรับว่าตัวอย่างอาจไม่ได้เป็นตัวแทน แต่คณิตศาสตร์ควรเป็นเรื่องเล็กน้อย
Rex Kerr

1

ในการพิสูจน์ว่ากราฟเป็นวัฏจักรคุณเพียงแค่ต้องพิสูจน์ว่ามันมีหนึ่งรอบ (ขอบชี้เข้าหาตัวเองไม่ว่าทางตรงหรือทางอ้อม)

ใน DFS เราใช้จุดยอดหนึ่งครั้งและตรวจสอบว่ามีวัฏจักรหรือไม่ ทันทีที่พบวัฏจักรเราสามารถละเว้นการตรวจสอบจุดยอดอื่น ๆ ได้

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


0

ขึ้นอยู่กับว่าคุณกำลังพูดถึงการใช้งานแบบวนซ้ำหรือแบบวนซ้ำ

Recursive-DFS เข้าชมทุกโหนดสองครั้ง Iterative-BFS เข้าชมทุกโหนดหนึ่งครั้ง

หากคุณต้องการตรวจจับวงจรคุณต้องตรวจสอบโหนดทั้งก่อนและหลังที่คุณเพิ่ม adjacencies ทั้งเมื่อคุณ "เริ่ม" บนโหนดและเมื่อคุณ "เสร็จสิ้น" ด้วยโหนด

สิ่งนี้ต้องการการทำงานมากขึ้นใน Iterative-BFS ดังนั้นคนส่วนใหญ่จึงเลือก Recursive-DFS

โปรดทราบว่าการใช้งาน Iterative-DFS แบบง่ายๆด้วยเช่น std :: stack มีปัญหาเช่นเดียวกับ Iterative-BFS ในกรณีนี้คุณต้องวางองค์ประกอบจำลองลงในสแต็กเพื่อติดตามเมื่อคุณ "เสร็จสิ้น" ในการทำงานบนโหนด

ดูคำตอบนี้สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่ Iterative-DFS ต้องการงานเพิ่มเติมเพื่อพิจารณาว่าเมื่อใดที่คุณ "เสร็จสิ้น" กับโหนด (ตอบในบริบทของ TopoSort):

การเรียงลำดับโทโพโลยีโดยใช้ DFS โดยไม่มีการเรียกซ้ำ

หวังว่าจะอธิบายได้ว่าทำไมผู้คนถึงนิยม Recursive-DFS สำหรับปัญหาที่คุณต้องพิจารณาเมื่อคุณ "เสร็จสิ้น" การประมวลผลโหนด


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

0

คุณจะต้องใช้BFSเมื่อคุณต้องการค้นหารอบที่สั้นที่สุดที่มีโหนดที่กำหนดในกราฟกำกับ

เช่น:ใส่คำอธิบายภาพที่นี่

ถ้าโหนดที่กำหนดคือ 2 มีสามรอบที่มันเป็นส่วนหนึ่งของ - [2,3,4], &[2,3,4,5,6,7,8,9] [2,5,6,7,8,9]สั้นที่สุดคือ[2,3,4]

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

แต่เพื่อวัตถุประสงค์อื่น ๆ ทั้งหมด (เช่นค้นหาเส้นทางที่เป็นวัฏจักรหรือเพื่อตรวจสอบว่ามีรอบหรือไม่) DFSเป็นทางเลือกที่ชัดเจนสำหรับเหตุผลที่ผู้อื่นกล่าว

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