เมื่อใดควรใช้กลยุทธ์ Preorder, Postorder และ Inorder Binary Search Tree Traversal


100

เมื่อเร็ว ๆ นี้ฉันตระหนักว่าในขณะที่ใช้ BST มากมายในชีวิตฉันไม่เคยคิดแม้แต่จะใช้อะไรเลยนอกจาก Inorder traversal (ในขณะที่ฉันตระหนักและรู้ว่าการปรับโปรแกรมให้ใช้การส่งผ่านคำสั่งก่อน / หลังการสั่งซื้อนั้นง่ายเพียงใด)

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

มีตัวอย่างอะไรบ้างในการใช้ preorder / postorder ในทางปฏิบัติ? เมื่อใดที่เหมาะสมกว่าตามลำดับ

คำตอบ:


141

เมื่อใดควรใช้กลยุทธ์การสั่งซื้อล่วงหน้าในการสั่งซื้อและหลังการสั่งซื้อ

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

รากของต้นไม้คือ7ซ้ายโหนดที่สุดคือ0 , โหนดที่เหมาะสมที่สุดคือ10

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

การสั่งซื้อล่วงหน้า :

สรุป: เริ่มต้นที่รูท ( 7 ) สิ้นสุดที่โหนดขวาสุด ( 10 )

ลำดับการข้ามผ่าน: 7, 1, 0, 3, 2, 5, 4, 6, 9, 8, 10

การส่งผ่านตามลำดับ :

สรุป: เริ่มต้นที่โหนดซ้ายสุด ( 0 ) สิ้นสุดที่โหนดขวาสุด ( 10 )

ลำดับการส่งผ่าน: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

การส่งผ่านภายหลังการสั่งซื้อ :

สรุป: เริ่มต้นด้วยโหนดซ้ายสุด ( 0 ) ลงท้ายด้วยรูท ( 7 )

ลำดับการข้ามผ่าน: 0, 2, 4, 6, 5, 3, 1, 8, 10, 9, 7

ควรใช้ Pre-Order, In-order หรือ Post-Order เมื่อใด

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

  1. หากคุณรู้ว่าคุณต้องสำรวจรากก่อนที่จะตรวจสอบใบใด ๆ คุณเลือกสั่งซื้อล่วงหน้าเพราะคุณจะพบรากทั้งหมดก่อนใบทั้งหมด

  2. หากคุณรู้ว่าคุณจำเป็นต้องสำรวจใบไม้ทั้งหมดก่อนโหนดใด ๆ คุณเลือกโพสต์ออร์เดอร์เพราะคุณไม่ต้องเสียเวลาตรวจสอบรากเพื่อค้นหาใบไม้

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

อัลกอริธึมแบบเรียกซ้ำสำหรับการสั่งซื้อล่วงหน้าตามลำดับและหลังการสั่งซื้อ (C ++):

struct Node{
    int data;
    Node *left, *right;
};
void preOrderPrint(Node *root)
{
  print(root->name);                                  //record root
  if (root->left != NULL) preOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) preOrderPrint(root->right);//traverse right if exists
}

void inOrderPrint(Node *root)
{
  if (root.left != NULL) inOrderPrint(root->left);   //traverse left if exists
  print(root->name);                                 //record root
  if (root.right != NULL) inOrderPrint(root->right); //traverse right if exists
}

void postOrderPrint(Node *root)
{
  if (root->left != NULL) postOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) postOrderPrint(root->right);//traverse right if exists
  print(root->name);                                   //record root
}

3
สิ่งที่เกี่ยวกับการข้ามผ่านแบบไม่เกิดซ้ำ? สำหรับฉันแล้วดูเหมือนว่ามันง่ายกว่ามากในการสำรวจต้นไม้แบบไม่ซ้ำในการสั่งซื้อล่วงหน้าเมื่อเทียบกับลำดับ / หลังการสั่งซื้อเนื่องจากไม่จำเป็นต้องกลับไปยังโหนดก่อนหน้า
bluenote10

@ bluenote10 ช่วยอธิบายให้ละเอียดหน่อยได้ไหมคะ? ในการสั่งซื้อล่วงหน้าคุณยังคง "กลับ" ไปยังโหนดเพื่อประมวลผลลูกที่ถูกต้องหลังจากประมวลผลลูกทางซ้าย แน่นอนว่าคุณสามารถใช้คิวของ "โหนดที่ยังไม่ได้เยี่ยมชม" แต่นั่นเป็นเพียงการแลกเปลี่ยนพื้นที่เก็บข้อมูลโดยนัย (สแต็ก) สำหรับคิวที่ชัดเจน ในวิธีการส่งผ่านทั้งหมดจะต้องมีการประมวลผลทั้งเด็กซ้ายและขวาซึ่งหมายความว่าหลังจากทำอย่างใดอย่างหนึ่งแล้วคุณจะต้อง "ส่งคืน" ให้ผู้ปกครอง
Joshua Taylor

@JoshuaTaylor: ใช่มันเป็นคลาสที่มีความซับซ้อนเหมือนกันทั้งหมด แต่ถ้าคุณดูการใช้งานทั่วไปการโพสต์ออร์เดอร์อาจจะยุ่งยากกว่าเล็กน้อย
bluenote10

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

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

34

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

ตามลำดับ::ใช้เพื่อรับค่าของโหนดในลำดับที่ไม่ลดลงใน BST

Post-order::ใช้เพื่อลบต้นไม้จากใบถึงราก


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

7
ตามลำดับ: เพื่อรับค่าของโหนดในลำดับที่ "ไม่ลดลง" ไม่ใช่ "ไม่เพิ่มขึ้น"
rahil008

26

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

- ROOT
    - A
         - B
         - C
    - D
         - E
         - F
             - G

4
หรือในTreeViewส่วนประกอบในแอปพลิเคชัน GUI
svick

4

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

สังเกตเพิ่มเติมว่าลำดับก่อนและหลังการสั่งซื้อร่วมกันระบุต้นไม้ที่อยู่ในมืออย่างสมบูรณ์โดยให้การเข้ารหัสที่กะทัดรัด (สำหรับต้นไม้ที่เบาบางน้อยที่สุด)


1
ฉันคิดว่าคุณกำลังพยายามพูดอะไรที่สำคัญคุณช่วยอธิบายครึ่งแรกได้ไหม
CodeYogi

@CodeYogi คุณต้องการอะไรเป็นพิเศษอธิบาย?
Raphael

1
"การสั่งซื้อล่วงหน้าและหลังการสั่งซื้อเกี่ยวข้องกับอัลกอริทึมการเรียกซ้ำจากบนลงล่างและจากล่างขึ้นบน" ฉันคิดว่าคุณต้องการบอกว่าในโหนดกรณีแรกจะถูกประมวลผลก่อนที่จะเรียกใช้วิธีการเรียกซ้ำใด ๆ และในทางกลับกันในวิธีหลังขวา เหรอ?
CodeYogi

@CodeYogi ใช่โดยพื้นฐานแล้ว
Raphael

2

มีสถานที่มากมายที่คุณเห็นความแตกต่างนี้มีบทบาทอย่างแท้จริง

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

x := y + 32

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

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

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