ฉันจะเลือกระหว่าง Hash Table กับ Trie (Prefix Tree) ได้อย่างไร


141

ดังนั้นถ้าฉันต้องเลือกระหว่างตารางแฮชหรือต้นไม้นำหน้าอะไรคือปัจจัยแบ่งแยกที่จะทำให้ฉันเลือกอย่างใดอย่างหนึ่ง จากมุมมองที่ไร้เดียงสาของฉันดูเหมือนว่าการใช้ Trie จะมีค่าใช้จ่ายพิเศษบางอย่างเนื่องจากไม่ได้จัดเก็บเป็นอาร์เรย์ แต่ในแง่ของเวลาทำงาน (สมมติว่าคีย์ที่ยาวที่สุดคือคำภาษาอังกฤษที่ยาวที่สุด) โดยพื้นฐานแล้วอาจเป็น O (1) (สัมพันธ์กับขอบเขตบน) บางทีคำภาษาอังกฤษที่ยาวที่สุดคือ 50 ตัวอักษร?

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

ใครช่วยให้มุมมองที่มีประสบการณ์มากกว่านี้แก่ฉันได้ไหม ขอบคุณ!


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

คำตอบ:


119

ข้อดีของการพยายาม:

พื้นฐาน:

  • เวลาค้นหา O (k) ที่คาดเดาได้โดยที่ k คือขนาดของคีย์
  • การค้นหาอาจใช้เวลาน้อยกว่า k หากไม่มี
  • รองรับการส่งผ่านตามคำสั่ง
  • ไม่จำเป็นต้องมีฟังก์ชันแฮช
  • การลบนั้นตรงไปตรงมา

การดำเนินการใหม่:

  • คุณสามารถค้นหาคำนำหน้าของคีย์ได้อย่างรวดเร็วระบุรายการทั้งหมดด้วยคำนำหน้าที่กำหนดเป็นต้น

ข้อดีของโครงสร้างที่เชื่อมโยง:

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

ข้อดีของแฮชแท็ก:

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

28
ไม่สามารถเห็นด้วยกับ "ประหยัดพื้นที่มากกว่าโครงสร้าง trie ที่เชื่อมโยงอย่างชัดเจน" - ในการใช้งานตารางแฮชทั่วไปจะใช้พื้นที่ขนาดใหญ่กว่ามากในการบรรจุคีย์ในขณะที่พยายามแต่ละโหนดจะแทนคำ ในแง่นี้การพยายามจะประหยัดพื้นที่มากกว่า
galactica

1
วิธีการเข้าถึงข้อมูลจากโครงสร้างหนึ่งกับอีกโครงสร้างหนึ่ง? ฉันกำลังคิดแคชและสถานที่
Horia Toma

9
@galactica ที่ขัดแย้งกับประสบการณ์ของฉัน: ตัวอย่างเช่นในคำตอบของโครงสร้างทั้งหมดที่ฉันวัดด้วยอวกาศสามคนมีอาการแย่ที่สุด สิ่งนี้สมเหตุสมผลเนื่องจากตัวชี้มีขนาดใหญ่กว่าไบต์มาก ใช่การแบ่งปันคำนำหน้าช่วยได้ แต่ต้องเอาชนะค่าใช้จ่ายจำนวนมากเพื่อให้บรรลุความเท่าเทียมกัน การแสดงพื้นที่ที่มีประสิทธิภาพมากขึ้นสามารถช่วยได้มาก แต่เราไม่ได้พูดถึงโครงสร้างที่เชื่อมโยงที่ชัดเจนอีกต่อไป
Darius Bacon

1
@DariusBacon แผนการจัดการหมายเลขโทรศัพท์ดูเหมือนเป็นสถานการณ์ที่สมเหตุสมผลสำหรับการพยายาม สถานการณ์ตัวอย่าง: หมายเลขโทรศัพท์ไปยังผู้ให้บริการที่ตรงกันรวมถึง หมายเลขที่โอนจากผู้ให้บริการรายหนึ่งไปยังอีกรายหนึ่ง สำหรับพจนานุกรมทั่วไปอาจขึ้นอยู่กับภาษา (จีนกลางเทียบกับอังกฤษ) คุณต้องมี n-gram และ / หรือข้อมูลทางสถิติอื่น ๆ สำหรับหนังสือสัมผัสต้นไม้คำต่อท้ายก็เป็นตัวเลือกที่ดีเช่นกัน
mbx

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

46

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


10
ถ้าตารางแฮชและ trie มีความซับซ้อนเหมือนกันในแบบสอบถาม O (k) สำหรับสตริงความยาว k ทำไมเราจึงควรใช้แฮช? คุณช่วยอธิบายได้ไหม
Sazzad Hissain Khan

30

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

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

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


11

ใช้ต้นไม้:

  1. หากคุณต้องการคุณสมบัติเติมอัตโนมัติ
  2. ค้นหาคำทั้งหมดที่ขึ้นต้นด้วย 'a' หรือ 'axe' เป็นต้น
  3. ต้นไม้ต่อท้ายเป็นรูปแบบพิเศษของต้นไม้ ต้นไม้คำต่อท้ายมีข้อดีทั้งหมดที่แฮชไม่สามารถครอบคลุมได้

5

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

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


3

การแทรกและการค้นหาบนสามเป็นเส้นตรงกับความยาวของสตริงอินพุต O (s)

แฮชจะให้ O (1) สำหรับการค้นหา ans การแทรก แต่ก่อนอื่นคุณต้องคำนวณแฮชตามสตริงอินพุตซึ่งอีกครั้งคือ O (s)

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

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

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

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


"แฮชจะให้ O (1) สำหรับการแทรกการค้นหา ans แต่ก่อนอื่นคุณต้องคำนวณแฮชตามสตริงอินพุตซึ่งอีกครั้งคือ O (s)" ขอบคุณที่อธิบายเรื่องนี้!
abadawi

การคำนวณฟังก์ชันแฮชไม่ใช่ O (s) มันเป็น O (1) จริงๆ คุณไม่จำเป็นต้องมีบิตทั้งหมดของสตริงในการคำนวณบางส่วน (จำนวนคงที่) ก็เพียงพอแล้ว
Nicola Amadio

2

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

PS:นอกเหนือจากนี้Ternary Search Trees (TSTs)จะเป็นตัวเลือกที่ยอดเยี่ยม เวลาในการค้นหามีมากกว่า HashTable แต่จะประหยัดเวลาในการดำเนินการอื่น ๆ ทั้งหมด นอกจากนี้ยังมีพื้นที่ที่มีประสิทธิภาพมากกว่าที่พยายาม


-2

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


6
ตารางแฮชส่วนใหญ่ไม่รับประกันเวลาดำเนินการที่ทราบกรณีที่เลวร้ายที่สุดคือ O (n) ถ้าทุกองค์ประกอบชนกันและถูกล่ามโซ่
Adam Rosenfield

2
สำหรับชุดข้อมูลใด ๆ คุณสามารถคำนวณฟังก์ชันแฮชที่สมบูรณ์แบบซึ่งจะรับประกันการค้นหา O (1) สำหรับข้อมูลนั้น แน่นอนว่าการคำนวณแฮชที่สมบูรณ์แบบนั้นไม่ฟรี
George V.Reilly

5
นอกจากนี้การล่ามโซ่ไม่ใช่วิธีเดียวที่จะจัดการกับการชน มีหลายวิธีที่น่าสนใจและชาญฉลาดในการจัดการสิ่งนี้เช่นการแฮชนกกาเหว่า ( en.wikipedia.org/wiki/Cuckoo_hashing ) สำหรับวิธีการหนึ่งและทางเลือกที่ดีที่สุดขึ้นอยู่กับความต้องการของรหัสไคลเอ็นต์
Hank Gay

ไม่รู้เกี่ยวกับการแฮชนกกาเหว่าและความสัมพันธ์กับตัวกรองบานจะทำให้การอ่านที่น่าสนใจขอบคุณ!
Horia Toma

อย่าลืมเกี่ยวกับ Robin-hood Hashing ซึ่งดีกว่าสำหรับแคชและความแปรปรวน sebastiansylvan.com/2013/05/08/… codecapsule.com/2013/11/11/robin-hood-hashing
Jarred Nicholls
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.