hashing-and-hash-tables
การทำความเข้าใจเกี่ยวกับ Hashing และ Hash Tables
Hashing และ Hash Tables เป็นเทคนิคที่ใช้ในการจัดเก็บข้อมูลเพื่อให้สามารถเข้าถึงได้อย่างมีประสิทธิภาพในเวลา O(1) ซึ่งเหมาะสำหรับการค้นหาหรือการจัดเก็บข้อมูลที่ต้องการการเข้าถึงอย่างรวดเร็ว
1. Hashing (การแฮช)
Hashing คือกระบวนการที่ใช้ในการแปลงข้อมูลที่มีขนาดใหญ่ (เช่น ข้อความ หรือ อ็อบเจ็กต์) ให้กลายเป็นค่าตัวเลข (เรียกว่า hash code หรือ hash value) ที่มีขนาดเล็กและเหมาะสม ซึ่งสามารถใช้ในการระบุตำแหน่งที่เหมาะสมในโครงสร้างข้อมูล เช่น Hash Table.
วิธีการทำงานของ Hashing:
- แปลงข้อมูลเป็นคีย์: ข้อมูลใดๆ (เช่น ชื่อผู้ใช้ หมายเลขประจำตัว) จะถูกแปลงเป็นตัวเลข (hash code) โดยใช้ ฟังก์ชันแฮช (hash function)
- การกำหนดตำแหน่งในการจัดเก็บ: ตัวเลขที่ได้จากฟังก์ชันแฮชจะถูกใช้เป็นดัชนีในการจัดเก็บข้อมูลในโครงสร้างข้อมูล
- การเข้าถึงข้อมูล: เมื่อเราต้องการเข้าถึงข้อมูลที่เก็บไว้ในโครงสร้างข้อมูล ก็จะใช้ค่าของคีย์เพื่อคำนวณตำแหน่งใน Hash Table และดึงข้อมูลออกมา
ฟังก์ชันแฮช (Hash Function):
ฟังก์ชันแฮชจะรับข้อมูลเข้าและแปลงเป็นค่าผลลัพธ์ที่เป็นตัวเลข โดยจะต้องมีคุณสมบัติดังนี้:
- ความเร็ว: ต้องสามารถคำนวณได้อย่างรวดเร็ว
- การกระจายตัว: ค่าผลลัพธ์ต้องกระจายตัวได้ดีเพื่อหลีกเลี่ยงปัญหาการชนกันของคีย์ (Collisions)
ตัวอย่างฟังก์ชันแฮชที่ง่ายที่สุดคือการใช้การหารค่าตัวเลขโดยใช้ตัวหาร (modulus):
hash_value = key % table_size
ตัวอย่างเช่น หากมีค่า key = 15 และ table_size = 10, ผลลัพธ์ของการแฮชจะเป็น 15 % 10 = 5
2. Hash Tables (แฮชเทเบิล)
Hash Table (หรือ Hash Map) เป็นโครงสร้างข้อมูลที่ใช้ Hashing ในการจัดเก็บข้อมูล โดยใช้ คีย์ เพื่อค้นหาหรือเข้าถึงข้อมูลที่เก็บไว้
โครงสร้างของ Hash Table:
- Buckets (หรือ Slots): ใช้สำหรับเก็บข้อมูล โดยแต่ละ bucket จะเก็บค่าที่สัมพันธ์กับคีย์
- Hash Function: ใช้ในการคำนวณตำแหน่งของข้อมูลใน hash table โดยการแปลงคีย์เป็นค่า hash
- Collision Handling: เมื่อมีการชนกัน (สองคีย์ได้ค่า hash เหมือนกัน) จะต้องมีวิธีจัดการการชนกัน เช่น การใช้ Chaining หรือ Open Addressing
วิธีการทำงานของ Hash Table:
- การแทรกข้อมูล (Insert): คำนวณตำแหน่งของข้อมูลโดยใช้ฟังก์ชันแฮช จากนั้นเก็บข้อมูลในตำแหน่งนั้น
- การค้นหาข้อมูล (Search): คำนวณตำแหน่งของข้อมูลจากคีย์ แล้วค้นหาข้อมูลใน bucket นั้น
- การลบข้อมูล (Delete): คำนวณตำแหน่งของข้อมูลจากคีย์ แล้วลบข้อมูลใน bucket นั้น
ปัญหาที่อาจเกิดขึ้น:
- Collisions (การชนกัน): เมื่อมีการแฮชคีย์สองตัวที่ได้ค่า hash เดียวกัน (มีตำแหน่งเดียวกันใน hash table)
- Chaining: ใช้รายการเชื่อมโยง (Linked List) เก็บข้อมูลที่ชนกันใน bucket เดียวกัน
- Open Addressing: ค้นหาตำแหน่งที่ว่างใน hash table เมื่อเกิดการชนกัน เช่น Linear Probing, Quadratic Probing หรือ Double Hashing
ตัวอย่างโครงสร้าง Hash Table:
สมมุติว่าเรามีข้อมูล:
- คีย์:
apple,banana,cherry - ค่าที่ต้องการเก็บ:
10,20,30
ฟังก์ชันแฮชอาจคำนวณเป็นดังนี้:
apple-> คีย์แฮช3banana-> คีย์แฮช1cherry-> คีย์แฮช0
ดังนั้น Hash Table อาจมีลักษณะดังนี้:
Index 0 -> cherry -> 30
Index 1 -> banana -> 20
Index 3 -> apple -> 10
3. วิธีการจัดการกับการชนกัน (Collision Handling)
1. Chaining
ในวิธีนี้ การชนกันของคีย์จะถูกจัดเก็บในลิสต์ที่เชื่อมโยงในแต่ละ bucket:
- หากมีการชนกันในตำแหน่งเดียวกัน (เช่น มีคีย์ที่แฮชออกมาเหมือนกัน), ข้อมูลจะถูกเก็บในลิสต์ที่เชื่อมโยง (Linked List) ใน bucket นั้น
- ข้อดี: สามารถจัดเก็บข้อมูลได้มากขึ้นในแต่ละ bucket
- ข้อเสีย: ต้องการพื้นที่หน่วยความจำเพิ่มเติมสำหรับลิสต์ที่เชื่อมโยง
2. Open Addressing
ในวิธีนี้ เมื่อเกิดการชนกัน ข้อมูลจะถูกเก็บในตำแหน่งที่ว่างใกล้ๆ:
- Linear Probing: หากเกิดการชนกันในตำแหน่งที่คำนวณได้, จะลองไปที่ตำแหน่งถัดไปที่ว่าง
- Quadratic Probing: การค้นหาตำแหน่งว่างจะทำโดยใช้ฟังก์ชันกำลังสอง
- Double Hashing: การใช้ฟังก์ชันแฮชสองครั้งในการคำนวณตำแหน่งถัดไป
4. ตัวอย่างการใช้งาน Hash Table (Python)
โค้ดตัวอย่างการใช้งาน Hash Table ด้วยการใช้ Dictionary ใน Python:
# สร้าง Hash Table (Dictionary)
hash_table = {}
# การแทรกข้อมูล
hash_table["apple"] = 10
hash_table["banana"] = 20
hash_table["cherry"] = 30
# การค้นหาข้อมูล
print(hash_table["apple"]) # ผลลัพธ์: 10
# การลบข้อมูล
del hash_table["banana"]
# การตรวจสอบข้อมูล
print("banana" in hash_table) # ผลลัพธ์: False
ผลลัพธ์:
10
False
การจัดการการชนกันใน Hash Table:
หากมีการชนกัน เช่น มีคีย์ที่แฮชออกมาเหมือนกัน (collision) ในการแทรกข้อมูล สามารถเลือกใช้ Chaining หรือ Open Addressing เพื่อจัดการการชนกันได้
5. ข้อดีและข้อเสียของ Hash Tables
ข้อดี:
- การเข้าถึงข้อมูลเร็ว: การเข้าถึงข้อมูลใน Hash Table โดยใช้คีย์สามารถทำได้ในเวลา O(1) (เฉลี่ย)
- การค้นหาที่มีประสิทธิภาพ: การค้นหาค่าใน Hash Table มีประสิทธิภาพสูง
- การจัดการข้อมูลที่ไม่ต้องเรียงลำดับ: ไม่จำเป็นต้องใช้ฟังก์ชันการเรียงลำดับในการจัดเก็บข้อมูล
ข้อเสีย:
- การจัดการการชนกัน: การชนกัน (collision) อาจทำให้การเข้าถึงข้อมูลช้าลง
- การใช้หน่วยความจำ: อาจต้องการพื้นที่หน่วยความจำเพิ่มเติมในการจัดเก็บข้อมูลและการจัดการการชนกัน
- ไม่เหมาะสำหรับข้อมูลที่มีลำดับ (Order): หากข้อมูลมีความสำคัญในการเรียงลำดับ Hash Table อาจไม่เหมาะสม
สรุป:
- Hashing เป็นกระบวนการแปลงข้อมูลให้เป็นค่าตัวเลขที่สามารถใช้ในการเข้าถึงข้อมูลใน Hash Table ได้อย่างรวดเร็ว
- Hash Table เป็นโครงสร้างข้อมูลที่ใช้ Hashing เพื่อจัดเก็บและค้นหาข้อมูลโดยใช้คีย์
- การจัดการการชนกันใน Hash Table สามารถทำได้โดยใช้ Chaining หรือ Open Addressing
- Hash Table เป็นโครงสร้างข้อมูลที่มีประสิทธิภาพสูงในการค้นหาและจัดเก็บข้อมูลที่ไม่จำเป็นต้องเรียงลำดับ