คุณต้องการแนะนำต้นไม้ไบนารีที่ปรับสมดุลตัวเองแบบใด


18

ฉันเรียนรู้ Haskell และออกกำลังกายฉันกำลังสร้างต้นไม้ไบนารี ต้องสร้างต้นไม้ไบนารีขึ้นมาเป็นประจำฉันต้องการที่จะปรับมันให้สมดุลกัน ดังนั้น:

  • ข้อใดมีประสิทธิภาพมากที่สุด
  • สิ่งที่ง่ายที่สุดที่จะใช้?
  • ใช้บ่อยที่สุด?

แต่อะไรที่คุณแนะนำ?

ฉันคิดว่าสิ่งนี้เป็นของที่นี่เพราะเปิดให้มีการอภิปราย


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

คำตอบ:


15

ฉันจะแนะนำให้คุณเริ่มต้นด้วยไม่ว่าจะเป็นต้นไม้สีแดงสีดำหรือต้นไม้ AVL

ต้นไม้สีแดงดำเร็วกว่าสำหรับใส่ แต่ต้นไม้ AVL มีขอบเล็กน้อยสำหรับการค้นหา ทรี AVL นั้นใช้ง่ายกว่าเล็กน้อย แต่ก็ไม่ได้ขึ้นอยู่กับประสบการณ์ของฉัน

ต้นไม้ AVL ช่วยให้มั่นใจได้ว่าต้นไม้มีความสมดุลหลังจากการแทรกหรือลบแต่ละครั้ง (ไม่มีต้นไม้ย่อยมีค่าสมดุลมากกว่า 1 / -1 ในขณะที่ต้นไม้สีแดงดำรับประกันว่าต้นไม้มีความสมดุลในเวลาใดก็ได้


1
โดยส่วนตัวแล้วฉันพบว่าเม็ดมีดสีแดงดำง่ายกว่าเม็ดมีด AVL เหตุผลก็คือผ่านการเปรียบเทียบ (ไม่สมบูรณ์) กับต้นไม้ B ส่วนแทรกนั้นเป็นตัวตลก แต่การลบนั้นเป็นสิ่งที่ชั่วร้าย (มีหลายกรณีที่ต้องพิจารณา) ในความเป็นจริงฉันไม่มี C ++ การใช้งานการลบสีแดงดำของตัวเองอีกต่อไป - ฉันลบมันเมื่อฉันรู้ (1) ฉันไม่เคยใช้มัน - ทุกครั้งที่ฉันต้องการลบฉันถูกลบหลายรายการดังนั้นฉันจึงแปลงจากต้นไม้เป็น รายการลบจากรายการจากนั้นแปลงกลับเป็นต้นไม้และ (2) มันถูกทำลายอยู่แล้ว
Steve314

2
@ Steve314 ต้นไม้สีแดงดำนั้นง่ายกว่า แต่คุณยังไม่สามารถใช้งานได้ ต้นไม้ AVL นั้นเป็นเช่นไร?
dan_waterworth

@dan_waterworth - ฉันยังไม่ได้ใช้งานด้วยแม้แต่วิธีการแทรกที่ใช้งานได้ - มีโน้ตเข้าใจหลักการพื้นฐาน แต่ไม่เคยมีการผสมผสานที่ลงตัวของแรงจูงใจเวลาและความมั่นใจ ถ้าฉันต้องการเวอร์ชั่นที่ใช้งานได้นั่นก็แค่ copy-pseudocode-from-textbook-and-translate (และอย่าลืม C ++ ที่มีไลบรารี่มาตรฐาน) แต่ความสนุกอยู่ที่ไหน?
Steve314

BTW - ฉันเชื่อว่า (แต่ไม่สามารถให้การอ้างอิงได้) ว่าหนังสือเรียนที่ได้รับความนิยมอย่างเป็นธรรมนั้นรวมถึงการใช้งานอัลกอริทึมแบบต้นไม้ไบนารีที่สมดุลอย่างใดอย่างหนึ่ง - ไม่แน่ใจ แต่อาจเป็นการลบสีแดงดำ ดังนั้นไม่ใช่แค่ฉัน ;-)
Steve314

1
@ Steve314 ฉันรู้ว่าต้นไม้สามารถมีความซับซ้อนอย่างโหดเหี้ยมในภาษาที่จำเป็น แต่น่าประหลาดใจที่การใช้มันใน Haskell นั้นเป็นเรื่องง่าย ฉันเขียนทรี AVL ปกติและตัวแปรอวกาศ 1D ช่วงสุดสัปดาห์และพวกมันทั้งคู่มีเพียงประมาณ 60 บรรทัด
dan_waterworth

10

ฉันจะพิจารณาทางเลือกถ้าคุณเป็นดีกับสุ่มโครงสร้างข้อมูล: รายการข้าม

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

คุณจะได้รับการแทรก / การค้นหา / ลบ O (บันทึก N) และคุณจะไม่ต้องจัดการกับกรณีที่ต้องทำการปรับสมดุลใหม่ทั้งหมด

ฉันไม่เคยคิดที่จะนำไปใช้ในภาษาที่ใช้งานได้และหน้าวิกิพีเดียไม่แสดงผลใด ๆ ดังนั้นจึงอาจไม่ใช่เรื่องง่าย (wrt to immutability)


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

นอกจากนี้ผู้คนมักใช้ skiplists สำหรับโครงสร้างข้อมูลที่เกิดขึ้นพร้อมกัน มันอาจจะดีกว่าแทนที่จะบังคับให้เปลี่ยนรูปไม่ได้เพื่อใช้การทำงานพร้อมกันของ Haskell (เช่น MVar หรือ TVar) แม้ว่ามันจะไม่สอนอะไรมากมายเกี่ยวกับการเขียนโค้ดฟังก์ชั่น
dan_waterworth

2
@ Fanatic23 รายการข้ามไม่ใช่ ADT ADT เป็นทั้งชุดหรืออาเรย์แบบเชื่อมโยง
dan_waterworth

@dan_waterworth ฉันไม่ดีคุณถูกต้อง
Fanatic23

5

หากคุณต้องการโครงสร้างที่ค่อนข้างง่ายในการเริ่มต้นด้วย (ทั้งต้นไม้ AVL และต้นไม้สีแดงดำจะเที่ยวยุ่งยิ่ง) หนึ่งทางเลือกคือ treap - ชื่อเป็นส่วนผสมของ "ต้นไม้" และ "กอง"

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

EDIT ลบ "ภายในค่าคีย์" ด้านบน - การเรียงลำดับความสำคัญและการเรียงลำดับคีย์ใช้ร่วมกันดังนั้นลำดับความสำคัญจึงมีความสำคัญแม้กับคีย์ที่ไม่ซ้ำกัน

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


1
+1 Treaps เป็นทางเลือกส่วนตัวของฉันฉันยังเขียนโพสต์บล็อกเกี่ยวกับวิธีการนำไปใช้
P Shved

5

ข้อใดมีประสิทธิภาพมากที่สุด

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

หากคุณหมายถึง "เวลาทำงาน" หรือ "การใช้หน่วยความจำ" คุณจะต้องเปรียบเทียบการใช้งานจริง จากนั้นภาษาเวลาทำงานระบบปฏิบัติการและปัจจัยอื่น ๆ เข้ามาทำให้คำถามยากที่จะตอบ

สิ่งที่ง่ายที่สุดที่จะใช้?

คลุมเครือและตอบยาก อัลกอริทึมบางอย่างอาจซับซ้อนสำหรับคุณ แต่ไม่สำคัญกับฉัน

ใช้บ่อยที่สุด?

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

แต่อะไรที่คุณแนะนำ?

ฉันคิดว่าสิ่งนี้เป็นของที่นี่เพราะเปิดให้มีการอภิปราย

แก้ไข. เนื่องจากเกณฑ์อื่น ๆ ของคุณไม่ค่อยมีประโยชน์นี่คือทั้งหมดที่คุณจะได้รับ

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

นี่คือรายการ:

http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree

มีการกำหนดยอดนิยมหกรายการ เริ่มต้นด้วยสิ่งเหล่านั้น


3

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

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

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

สิ่งหนึ่ง - เนื่องจากโครงสร้างข้อมูลตามคำจำกัดความเปลี่ยนแปลงแม้กระทั่งสำหรับการดำเนินการค้นหามันอาจจะต้องมีการดำเนินการ monadically IOW มันอาจจะไม่เหมาะสำหรับการเขียนโปรแกรมฟังก์ชั่น


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

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

ทั้งวิธีการฟังดูคุ้มค่ากับความพยายาม จากนั้นอีกครั้งไม่ได้ทำหน้าที่ภาษาล้วน ๆ ส่วนใหญ่
โจสมิ ธ อย่างรวดเร็ว

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

2

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

ในการฝึกขั้นสูงคุณสามารถลองใช้GADTเพื่อใช้หนึ่งในสายพันธุ์ของทรีที่สมดุลซึ่งค่าคงที่นั้นถูกบังคับใช้โดยชนิดของระบบ

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