ตารางแฮชกับต้นไม้ไบนารี


30

เมื่อติดตั้งพจนานุกรม ('ฉันต้องการค้นหาข้อมูลลูกค้าตามรหัสลูกค้า') โครงสร้างข้อมูลทั่วไปที่ใช้คือตารางแฮชและแผนภูมิการค้นหาแบบไบนารี ฉันรู้ว่าอินสแตนซ์ที่ห้องสมุด C ++ STL ใช้พจนานุกรม (เรียกพวกเขาว่าแผนที่) โดยใช้แผนภูมิการค้นหาแบบทวิภาค (สมดุล) และกรอบงาน. NET ใช้ตารางแฮชภายใต้ประทุน

ข้อดีและข้อเสียของโครงสร้างข้อมูลเหล่านี้คืออะไร มีตัวเลือกอื่นที่เหมาะสมในบางสถานการณ์หรือไม่?

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


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

@jmad เขาบอกว่าเขาไม่สนใจในกรณีนั้น
Joe

@ โจฉันคิดว่ามันชัดเจนว่าฉันนำสิ่งนี้มาพิจารณา อย่างไรก็ตามนั่นไม่ใช่เหตุผลที่จะให้ตัวอย่างที่สำคัญที่สุดที่เป็นไปได้
jmad

1
จริงๆแล้ว. NET มีทั้งพจนานุกรมที่ใช้งานโดยใช้แผนผังและพจนานุกรมที่ใช้งานโดยใช้ตารางแฮช (และ C ++ เป็นต้นมาตั้งแต่ปี 2011)
sepp2k

เป็นไปได้เช่นเดียวกันกับ SO: stackoverflow.com/questions/371136/ …
Ciro Santilli 事件改造中心中心法轮功六四事件

คำตอบ:


26

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

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

สมดุลต้นไม้ค้นหาแบบทวิภาคมีความซับซ้อนค่อนข้างสม่ำเสมอ: แต่ละองค์ประกอบจะใช้เวลาหนึ่งโหนดในต้นไม้ (ปกติ 4 คำพูดของหน่วยความจำ) และการดำเนินงานพื้นฐาน (การค้นหาแทรกลบ) ใช้เวลาเวลา (รับประกัน asymptotic ขอบเขตบน) แม่นยำยิ่งขึ้นการเข้าถึงต้นไม้จะใช้เวลาประมาณl o g 2 ( n )การเปรียบเทียบO(ล.ก.(n))ล.โอก.2(n)

ตารางแฮชเป็นตัวแปรอีกเล็กน้อย พวกมันต้องการพอยน์เตอร์ประมาณอยน์เตอร์ การเข้าถึงองค์ประกอบหนึ่งขึ้นอยู่กับคุณภาพของฟังก์ชันแฮช วัตถุประสงค์ของฟังก์ชั่นแฮชคือการแยกองค์ประกอบออก ตารางแฮช“ ทำงาน” หากองค์ประกอบทั้งหมดที่คุณต้องการจัดเก็บมีแฮชต่างกัน หากเป็นกรณีนี้การดำเนินการพื้นฐาน (การค้นหาการแทรกการลบ) ใช้เวลาO ( 1 )กับค่าคงที่ที่ค่อนข้างเล็ก (การคำนวณหนึ่งแฮชบวกการค้นหาตัวชี้หนึ่งครั้ง) ทำให้ตารางแฮชเร็วมากในหลายกรณีทั่วไป2nO(1)

ปัญหาทั่วไปเกี่ยวกับตารางแฮชคือไม่รับประกันความซับซ้อนO(1)

  • นอกจากนี้ยังมีจุดที่ตารางเต็ม เมื่อสิ่งนั้นเกิดขึ้น (หรือดีกว่าก่อนเกิดเหตุการณ์เล็ก ๆ น้อย ๆ ) ตารางจะต้องมีการขยายซึ่งต้องมีการเคลื่อนย้ายองค์ประกอบทั้งหมดเพื่อต้นทุนสิ่งนี้สามารถแนะนำพฤติกรรม“ กระตุก” เมื่อมีการเพิ่มองค์ประกอบจำนวนมากO(n)
  • เป็นไปได้ที่อินพุตจะชนกันกับค่าแฮชสองสามตัว สิ่งนี้ไม่ค่อยเกิดขึ้นตามธรรมชาติ แต่มันอาจเป็นปัญหาด้านความปลอดภัยหากอินพุตถูกเลือกโดยผู้โจมตี: มันเป็นวิธีที่ทำให้เซิร์ฟเวอร์บางเครื่องช้าลงอย่างมาก ปัญหานี้ได้นำการใช้งานภาษาการเขียนโปรแกรมบางอย่าง (เช่น Perl และ Python) เพื่อเปลี่ยนจากตารางแฮชเก่าธรรมดาไปเป็นฟังก์ชันแฮชที่เกี่ยวข้องกับหมายเลขสุ่มที่เลือกเมื่อสร้างตารางแฮชพร้อมกับฟังก์ชันแฮชที่กระจายตัวเลขสุ่มแบบนี้ (ซึ่งจะเพิ่มค่าคงที่การคูณใน ) หรือไปยังแผนภูมิการค้นหาแบบไบนารี ในขณะที่คุณสามารถหลีกเลี่ยงการชนกันโดยใช้แฮชการเข้ารหัสลับนี้ไม่ได้ทำในทางปฏิบัติเพราะแฮชการเข้ารหัสลับนั้นค่อนข้างช้าในการคำนวณO(1)

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

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

k1k2ชั่วโมง(k1)=ชั่วโมง(k2)

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

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

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


2
BSTs ไม่มีข้อมูลท้องถิ่นที่ไม่ดีด้วยหรือไม่
svick

@svick พวกเขาอาจหรืออาจจะไม่ขึ้นอยู่กับวิธีการจัดสรรโหนด การเพิ่ม arity ของต้นไม้สามารถช่วยได้โดยไม่สูญเสียเวลาในการทำงาน (ค่าใช้จ่ายจะมากและซับซ้อนกว่าโค้ด)
Gilles 'ดังนั้นหยุดความชั่วร้าย'

2
ใน BST มันเป็นเรื่องง่ายที่จะได้รับองค์ประกอบ "ตามลำดับ" สำหรับตารางแฮชมันเป็นไปไม่ได้
vonbrand

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

@ Kyth'Py1k คุณหมายถึงอะไร "ต้นไม้ที่จะทำให้เสร็จ"? จุดของตารางแฮชคือการเข้าถึงทีละค่าไม่ใช่ทั้งแผนผังมิฉะนั้นรายการหรืออาร์เรย์จะทำงานได้ดีขึ้น แม้ใน situtations ที่ค่าเฉลี่ยคือสิ่งที่สำคัญ (ซึ่งไม่ได้เป็นเช่นทุกครั้งเช่นเมื่อคุณมีข้อ จำกัด เรียลไทม์) มันเป็นค่าเฉลี่ยมากกว่าคำขอที่ทำในสถานการณ์ที่กำหนดซึ่งมักจะไม่เหมือนกันทั่วตาราง - เช่นเอนเอียงไปยังคำนำหน้าบางอย่าง
Gilles 'หยุดความชั่วร้าย' Gilles
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.