26
Hash Table เป็ นโครงสร ้างชนิดหนึ่งที่ช่วยให ้การนาข ้อมูลเข ้า และการค ้นหาข ้อมูลทาได ้รวดเร็ว ขึ้น (เร็วกว่าการใช tree) ในบทนี้เราจะมาทาความรู ้จักกับวิธีการใช ้ Hash Table ในการจัดการ กับข ้อมูล หลังจากจบบทเรียนนี้แล ้วผู ้อ่านจะได ้ทราบถึง o Hashing o Collision (index ที่ซ้ากัน) o การแก ้ปัญหาของ collision ด ้วยวิธี o Open Addressing o Linear Probing o Quadratic Probing o Double Hashing o Separate Chaining o การใช ้ Hash function ต่าง ๆ o การใช ้ Hash table ทีJava มีให ้ o Dynamic hashing 10.1 Hashing Hashing เป็ นเทคนิคอีกแบบหนึ่งที่เรานามาใช ้ในการเก็บข ้อมูลที่มีจานวนมาก ๆ และต ้องการ การค ้นหาที่รวดเร็ว เช่นการเก็บคาศัพท์ในพจนานุกรม การเก็บข ้อมูลของลูกค ้า หรือการเก็บ ข ้อมูลหนังสือต่าง ๆ ที่มีอยู่ในห ้องสมุด สมมติว่าเราต ้องการเก็บคาว่า bike ไว ้ในพจนานุกรม เรา อาจทาได ้ด ้วยวิธีกาหนดค่าของตัวอักษร แต่ละตัวที่มีอยู่ในภาษานั้น ๆ (ในที่นี้จะใช ้ตัวอักษร ภาษาอังกฤษ) แล ้วจึงนาเอาผลรวมของค่าเหล่านั ้นมาเป็ น index เข ้าหา array ที่ใช ้เก็บคา เหล่านีb = 2 i = 9 k = 11 e = 5 และเมื่อรวมกันแล ้วก็ได ้ เท่ากับ 2 + 9 + 11 + 5 = 27 เราก็เก็บคาว่า bike ไว ้ใน array ตาแหน่งที่ 27 คาอื่น ๆ ที่มีอยู่ก็จะใช ้การเก็บในรูปแบบนี้ แต่ การเก็บแบบนี้ทาให ้เกิดปัญหา มีคามากมายที่เมื่อหาผลรวมแล ้วมีค่าเท่ากับ 27 (คาว่า kibe ก็ เป็ นหนึ่งในนั้น) เราต ้องหาวิธีอื่นที่จะทาให ้เราสามารถเก็บคาได ้มากกว่าที่เป็ นอยูปัญหาอีก อันหนึ่งที่ตามมาคือ เนื้อที่ในการใช ้เก็บคาเหล่านี้ เราต ้องใช ้ array ขนาดที่ใหญ่มากเพื่อรองรับ ค่าทั้งหมด สมมติว่าเราต ้องการเก็บค่าระหว่าง 0 199 ไว ้ใน array แต่เรามีเนื้อที่จากัดเราอาจใช ้ modulus operator (%) ในการหา index ที่ใช ้ในการเก็บข ้อมูลเหล่านี้ ซึ่งทาให ้เราสามารถลด ขนาดของ array ลงได ้ถึง 20 ต่อ 1

Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table เปนโครงสรางชนดหนงทชวยใหการน าขอมลเขา และการคนหาขอมลท าไดรวดเรวข น (เรวกวาการใช tree) ในบทนเราจะมาท าความรจกกบวธการใช Hash Table ในการจดการกบขอมล หลงจากจบบทเรยนนแลวผอานจะไดทราบถง o Hashing

o Collision (index ทซ ากน) o การแกปญหาของ collision ดวยวธ o Open Addressing

o Linear Probing o Quadratic Probing o Double Hashing

o Separate Chaining o การใช Hash function ตาง ๆ o การใช Hash table ท Java มให o Dynamic hashing 10.1 Hashing

Hashing เปนเทคนคอกแบบหนงทเราน ามาใชในการเกบขอมลทมจ านวนมาก ๆ และตองการ การคนหาทรวดเรว เชนการเกบค าศพทในพจนานกรม การเกบขอมลของลกคา หรอการเกบขอมลหนงสอตาง ๆ ทมอยในหองสมด สมมตวาเราตองการเกบค าวา bike ไวในพจนานกรม เราอาจท าไดดวยวธก าหนดคาของตวอกษร แตละตวทมอยในภาษานน ๆ (ในทนจะใชตวอกษรภาษาองกฤษ) แลวจงน าเอาผลรวมของคาเหลานนมาเปน index เขาหา array ทใชเกบค าเหลาน b = 2 i = 9 k = 11 e = 5 และเมอรวมกนแลวกได เทากบ 2 + 9 + 11 + 5 = 27 เรากเกบค าวา bike ไวใน array ต าแหนงท 27 ค าอน ๆ ทมอยกจะใชการเกบในรปแบบน แตการเกบแบบนท าใหเกดปญหา มค ามากมายทเมอหาผลรวมแลวมคาเทากบ 27 (ค าวา kibe กเปนหนงในนน) เราตองหาวธอนทจะท าใหเราสามารถเกบค าไดมากกวาทเปนอย ปญหาอก

อนหนงทตามมาคอ เนอทในการใชเกบค าเหลาน เราตองใช array ขนาดทใหญมากเพอรองรบคาทงหมด สมมตวาเราตองการเกบคาระหวาง 0 – 199 ไวใน array แตเรามเนอทจ ากดเราอาจใช modulus operator (%) ในการหา index ทใชในการเกบขอมลเหลาน ซ งท าใหเราสามารถลดขนาดของ array ลงไดถง 20 ตอ 1

Page 2: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

254

เรารวาคาระหวาง 0 – 199 เมอหารดวย 10 แลวจะใหคาทอยระหวาง 0 – 9 เชน 15 % 10 = 5 หรอ 198 % 10 = 8 เปนตน เพราะฉะนนเรากสามารถใชเทคนคนในการหา index ของ array ได index = number % size; ตวอยางทเหนนเปนตวอยางของ hash function ตวหนงซ งเปนการท าใหคาทอยใน range1 มาก ๆ ลงมาอยใน range เลก ๆ ได และ array ทใช hash function ในการค านวณหา index ของ array เราเรยกวา hash table ตวอยางภาพทเหนน แสดงการเกบขอมลของพนกงาน (เลก ๆ) ทใช id number ของพนกงานเปนตวหา index เชน พนกงานทม id เปน 341 จะถกเกบไวท array ชองทหนง และพนกงานทม id ดงตอไปนกจะถกเกบไวใน index ทหาได

253 % 10 = 3 308 % 10 = 8 319 % 10 = 9

ภาพท 10.1 Hash table

การเขาหาขอมลใน array นนใชเวลาเพยงนอยนดหากเรารวา index ของขอมลนนคออะไร กระบวนการในการทจะใหไดมาซงคาของ index ทดนนเปนอกเรองหนง เราจะมาดถงการใช hash function อยางงาย ๆ เพอใหเหนถงการจดการกบขอมลทใช array และ index ของ array 10.2 index ทซ ำกน (Collision) ถงแมวาเราสามารถทจะใชเนอทนอยลงในการเกบ index ตาง ๆ (รวมไปถงการใชเวลาในการคนหาทนอยลง) แตเรากสรางปญหาใหมข นมาอก นนกคอ การ hash อาจท าใหเกด index ทซ ากน เชนสมมตวาเราม พนกงานอกคนหนงทม id = 333 เรากจะได index ทซ ากนกบของพนกงานทม id เปน 253 ท าใหเราตองหาต าแหนงใหมใหกบพนกงานคนนในตารางเกบขอมล 10.2.1 วธแกปญหำ index ทซ ำกน วธการแกปญหาวธนใชเนอท ทมอยใน array เปนเนอททจะน าเอาขอมลท hash ออกมาทเดยวกนไปใสไว โดยจะท าการคนหาจนกวาจะเจอ มวธอย 4 วธ คอ linear probing, quadratic probing, double hashing, และ key offset

1 Range หมายถงกลมของตวเลขกลมใดกลมหนง เชน ถาพดวา x อยใน range ของ 1 – 100 คาของ x จะเปนอยางอนไมไดนอกเหนอจากคาทอยในกลมน หรอทพดวา x อยระหวาง 1 - 100 (1 ≤ x ≤ 100)

341

253

308

319

01

02

03

07

09

08

06

04

05

341

253

308

319

John

Robert

Fred

Peter

00

Page 3: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

255

10.2.1.1 Linear Probing เราแกไขปญหาดวยการเพมคาใหกบ index ท hash ลงทเดยวกน วธการเพมคาอาจเพมทละ 1 คา หรอกคากไดทเหนวาเหมาะสม ตวอยางทแสดงในภาพใชการเพมทละ 1 คา เราตองการทจะใสพนกงานหมายเลข 312 และ 222 เขาส Hash table ของเรา ในการใสครงแรกนนไมมปญหา แตการใสครงทสองเราได index ซ ากนกบของพนกงานหมายเลข 312 เราจงเพมคาใหกบ index เปน 3 แตเรากยงเจอปญหาอย เรากเพมใหอกหนงคาเปน 4 ซงต าแหนงนเราสามารถทจะใสพนกงานหมายเลข 222 ได

ภาพท 10.2 Linear probing

ในการเพมคาใหกบ index ทซ ากนนน เราจะตองตรวจสอบดวยวา index ใหมทไดมาอยใน range ของ table หรอไม และในการหา index นนเราจะตองก าหนดใหมการวนของ index เชนถาเราได index 9 เราตองสามารถทจะก าหนดใหการน าขอมลเขาใน index 0 ได (เชนเดยวกบทเราท าใน circular array) Linear probing งายตอการ implement และขอมลจะไมอยหางจากจดเรมตนของการ hash มากมายนก ซงท าใหงายตอการเขาหา (ในกรณของการเขยนขอมลลง disk) แตจดดอยของ linear probing กคอเมอขอมลอยรวมกนมาก ๆ (ดวยคา hash เดยวกน และ/หรอเมอมการเพมคา index เพอหาทเกบขอมลทวางอย) หรอทเรยกวา clustering (รหส 01 – 04 ในภาพท 10.2) การ probe จะมมากขน และเมอขอมลถกลบออก การคนหากมความซบซอนมากยงข น เพอใหงายตอความเขาใจ เราจะใช class Employee เปนตนแบบขอมลทเราจะน าไปเกบไวใน Hash table โดยจะใช id เปนตวก าหนดต าแหนงในการจดเกบ

1: /**

2: employee record for hash table

3: */

4:

5: class Employee {

6: private int id;

7: private int collision;

8: private String name;

9:

10: //default constructor

11: Employee(int id, String name) {

12: this.id = id;

13: this.name = new String(name);

14: }

15:

16: //return this id

17: public int getId() {

18: return id;

19: }

20:

21: //return this name

Probe 1

Probe 2

01

02

03

07

09

08

06

04

05

341

312

253

222

308

319

John

Robert

Fred

Peter

Jay

Jennie

222

312

First insert – No Collision

Second insert - Collision

00

Page 4: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

256

22: public String getName() {

23: return name;

24: }

25:

26: //set collision

27: //for displaying purpose only

28: public void setCollision() {

29: collision++;

30: }

31:

32: //return this collision of this

33: //employee's slot

34: public int getCollision() {

35: return collision;

36: }

37:

38: //display employee's info

39: public String toString() {

40: StringBuffer buffer = new StringBuffer();

41: buffer.append(id + "\t" + name + "\n");

42: return new String(buffer);

43: }

44: }

และเนองจากวาเราตองการทจะแสดงถง collision ทอาจเกดขนเราจงเพมตวแปร collision ส าหรบการเกบจ านวนครงของ collision ทเกดข นพรอมกบการสราง method setCollision() และ getCollision() ส าหรบการก าหนดจ านวนของ collision และการดงจ านวน collision มาใช (ตามล าดบ) [กำรน ำขอมลเขำ] ในการน าขอมลเขาส Hash table นนเราตองหาต าแหนงดวยการหาคาของ index ดวย id ของพนกงานทเราจะน าเขาโดยสงคา id นไปให hashFunc() หลงจากทเราไดต าแหนง (hashValue) แลวเราตองตรวจสอบต าแหนงทไดมาวามขอมลอยกอนแลวหรอไม ดวยการเรยก linearProbe() ซง linearProbe() จะท าการหาต าแหนงใหใหมถาเกด collision ดวยวธของ linear probing ทเราไดกลาวไวกอนหนาน และเมอไดต าแหนงแลวเรากน าขอมลพนกงานคนนไปจดเกบตอไป public void insertLinear(Employee emp) {

int key = emp.getId(); //use emp.id as key

int hashValue = hashFunc(key); //generate hash key

//find available slot for insertion

hashValue = linearProbe(hashValue);

table[hashValue] = emp;

}

Method hashFunc() ทเราใชเปน hash function อยางงาย ๆ public int hashFunc(int key) {

return key % size;

}

สวน Method linearProbe() ทเราเขยนขนเพอแกปญหาของ collision นนมดงน private int linearProbe(int hashValue) {

//assuming that table is not full

while(table[hashValue] != null && table[hashValue].getId() != -1) {

table[hashValue].setCollision(); //collisions

++hashValue;

hashValue %= size;

}

return hashValue;

}

เราจะท าการบนทกจ านวนครงของ collision ทเกดข น (เพอแสดงผล) ดวยประโยค

Page 5: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

257

table[hashValue].setCollision(); //collisions

การน าขอมลเขาส Hash table ของเรานนเราจะตงสมมตฐานวา Hash table ของเราไมเตม ซงผอานสามารถทจะปรบปรงตอไปเพอให Hash table สามารถรองรบขอมลไดเรอย ๆ ดงเชนทเราไดท าไวในเรองของ Dynamic array แตผอานควรตงขอสงเกตใหดวาในการหาต าแหนงของ array ใหมทเราไดขยายออกนน จะตองท าการ hash ใหมทงหมด ทงนกเพราะวาเราใชขนาดของ array เปนตวหาต าแหนงทขอมลตองจดเกบ เราใชโปรแกรม TestHashTable.java2 เปนตวทดสอบการจดเกบทใช Linear probing เปนการแกปญหาของ collision หลงจากทเราได run โปรแกรมไดสกพกหนง ผลลพธทเราไดคอ Slot Id. Name

0 19 Again

1 12 Lovers (collision 1)

2 23 Noels

3

4

5

6

7 84 dummy (collision 2)

8 18 Paul (collision 2)

9 20 dummy (collision 2)

10 29 Joop (collision 1)

เราเรมตนดวยขอมลเบองตน 2 ตวคอ (84, dummy) และ (20, dummy) หลงจากนนเรากน า (18, Paul) เขาซงท าใหเกด collision ครงทหนงตอมาเราน า (12, Lovers) เขาโดยไมม collision และเมอเราน า (29, Joop) เขากเกด collision อกท id = 84, 18, และ 20 และอกครงหนงเมอน า (23, Noels) เขา ผลลพธทเราแสดงใหดไมมการคนหา หรอลบขอมลแตอยางใด จะทงไวใหผอานไดทดลองท าดเอง ซงในการลบขอมลออกนนเราจะก าหนดใหคาของ id ณ ต าแหนงนนเปน -1 และ name มคาเปน dummy 10.2.1.2 Quadratic Probing เพอไมใหเกดการรวมกนของกลมขอมลท hash ลงทเดยวกน (clustering) เชน slot ท 7, 8, 9, และ 10 จากตวอยางของผลลพธกอนหนาน เราสามารถทจะกระจายขอมลท hash ลงทเดยวกนดวยการใช เลขยกก าลงสองเปนตวก าหนด index ของ hash table เชนถาการ probe ครงแรกดวย 12 ท าใหเกด collision เราก probe ครงทสอง ดวย 22 ถาเจอ collision อกเราก probe ครงทสามดวย 32 ท าไปเรอย ๆ จนกวาจะเจอต าแหนงทไมม collision สมมตวา index ทเราหาไดครงแรกมคาเปน 7 แตต าแหนงนมขอมลอยแลวเรากหาใหมดวย index = index + 12 (คาใหมคอ 8) ถา index ใหมนมขอมลอยเรากหาใหมดวย index = index + 22 (คาใหมคอ 11) ถามขอมลอกเรากหาใหมดวย index = index + 32 (คาใหมคอ 16) และใชวธการเดยวกนนถาเกด collision อกจนกวาจะเจอต าแหนงทตองการ ซงต าแหนงทเกดข นใหมนจะอยในรปของการเพมทละ 1, 4, 9, 16, 25, … ไปเรอย ๆ ท าใหขอมลกระจายกนอยในตาราง ลดการรวมกลม (cluster) ของขอมล public void insertQuad(Employee emp) {

int key = emp.getId(); //use emp.id as key

int hashValue = hashFunc(key); //generate hash key

//find available slot for insertion

hashValue = quadraticProbe(hashValue);

table[hashValue] = emp;

}

2 code ทงหมดเราไดจดแสดงไวในตอนทายของเรอง

Page 6: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

258

private int quadraticProbe(int key) {

while(table[key] != null && table[key].getId() != -1) {

table[key].setCollision();

//increment step

key += table[key].getCollision() * table[key].getCollision();

key %= size;

}

return key;

}

ส าหรบ code ของ Quadratic probe ทเหนดานบนน เรากยงตงสมมตฐานเดมคอ Hash table นนไมเตม และเราหาต าแหนงใหมดวยประโยค key += table[key].getCollision() * table[key].getCollision();

key %= size;

ผลลพธของการ run โปรแกรม (บางสวน) ดวย Quadratic probing Slot Id. Name

0 20 James (collision 4)

1 10 Paul

2

3

4 30 Peep

5

6 40 Once

7

8

9 50 Again

จากผลลพธจะเหนวาเมอม collision เกดข น (index = 0) ในครงแรกนนต าแหนงใหมทเราได ส าหรบ id = 10 คอ 1 แตเมอเราตองการน า id = 30 เขาเราไดต าแหนง index = 4 (index = 30 % 10 + 22) ตอมา index ส าหรบ id = 50 คอ 9 (index = 50 % 10 + 33) และต าแหนงส าหรบ id = 40 คอ index = 6 10.2.1.3 Double Hashing (Pseudorandom) ถาการ hash ครงแรกท าใหเกด collision เรากจะ hash อกครงหนง มวธอย 2 วธคอ การใช pseudorandom number และการใช key offset ในการใช pseudorandom นนเราจะใชสมการทสราง random number ใหอยางงาย ๆ เชน การใช สมการ y = ax + c ซงถาเราก าหนดให a มคาเปน 3 และ c มคาเปน 1 การ hash ใหมของเรากเปน y = (ax + c) % size y = (3 x 2 + 1) % 10 = 7

Page 7: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

259

ภาพท 10.3 Pseudorandom y = 3x + c

เราอาจก าหนดใหม hash function อกตวหนงกไดถาเราตองการ แต hash function ตวนจะตองไมสราง index ท hash function ตวเกาไดสรางไว และจะตองไมสรางคาทเปน 0 เพราะจะท าใหการ probe เจอต าแหนงทซ ากนตลอดเวลา โดยทวไป hash function ตวท 2 มกใช hashValue = constant – (key % constant)

โดยก าหนดให คา constant เปน prime ทมขนาดเลกกวาขนาดของ table เชน hashValue = 5 – (key % 5)

ซงเปนการหา hash key ท expert หลาย ๆ คนยอมรบ การเรยกใชกท าไดโดยการเรยก hash function ตวแรกเพอใหได hash key กอนแลวจงน าเอาคาทไดจากการ hash ครงทสองมารวมเขากบคาน hashValue = hashFunc(key); hashValue = hashFunc(222) = 2

step = hashFunc2(key); step = hashFunc2(222) = 5 – (222 % 5) = 3

hashValue += step; hashValue = 2 + 3 = 5

ภาพท 10.4 Double Hashing – hashFunc() and hashFunc2()

ลองมาด code ของ double hashing ด

01

02

03

07

09

08

06

04

05

341

312

253

222

308

319

John

Robert

Fred

Jay

Peter

Jennie

222

312

Second insert - Collision

First insert – No Collision

00

Double Hashing

01

02

03

07

09

08

06

04

05

341

312

253

308

222

319

John

Jay

Robert

Fred

Peter

Jennie

222

312

Pseudorandom y = 3x + 1 Second insert

- Collision

First insert – No Collision

00

Page 8: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

260

//hash function

public int hashFunc(int key) {

return key % size;

}

//second hash function

public int hashAgain(int key) {

return 5 - key % 5;

}

//double hashing using pseudorandom number

public int hashTwice(int hashValue, int step) {

//assuming that table is not full

while(table[hashValue] != null) {

table[hashValue].setCollision(); //collisions

hashValue += step;

hashValue %= size;

}

return hashValue;

}

//insert employee into table

//using Double hashing for collision

public void insertTwice(Employee emp) {

int key = emp.getId(); //use emp.id as key

int hashValue = hashFunc(key); //generate hash key

int step = hashAgain(key); //second hash

//find available slot for insertion

hashValue = hashTwice(hashValue, step);

table[hashValue] = emp;

}

หลงจากททดสอบดวยโปรแกรมเดม เราไดผลลพธดงทเหนน

Slot Id. Name

0 11 Paul (collision 3)

1 44 Once

2 33 Roland

3 22 John

4

5 16 Again (collision 1)

6

7

8 27 De los Santos

9

10

พรอม ๆ กนกบการท hash() ตวทสองตองไมสราง index ทเหมอนกบตวแรกแลว hash() ตวทสองจะตองไมสราง index ทมคาเปน 0 เพราะจะท าใหการท างานของ loop ไมมจดส นสด และทส าคญมาก ๆ ทเราเนนอยตลอดเวลากคอขนาดของ table ควรเปนจ านวนทเปนเลข prime (เลขทหารดวยตวเองและหนงลงตวเทานน) 10.2.1.4 Double Hashing (Key Offset) วธอกวธหนงกคอการใช key offset และขนตอนทงายตอการ implement กคอ การบวกสวน (quotient) ทไดจากการหาร key ดวยขนาดของ table กบ address เกา เชน offset = key / size

address = (offset + old address) % size

ถา key = 222 และ address เกาคอ 2 เรากจะได offset = 222 / 10 = 22

address = (22 + 2) % 10 = 4

แตถา address ใหมทไดท าใหเกด collision อกเรากเรมกระบวนการใหมอกครง

Page 9: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

261

offset = 222 / 10 = 22

address = (22 + 4) % 10 = 6

เรากใช address ใหมนในการจดเกบขอมลตอไป ดงแสดงในภาพท 11.5

ภาพท 10.5 Key offset Probe

การเขยน code เพอรองรบการแกปญหาของ collision ดวย key offset กไมยากดงทไดแสดงใหดน //key-offset

private int keyOffset(int index, int key) {

int offset;

//assuming that table is not full

while(table[index] != null) {

table[index].setCollision();

offset = key / size;

index = (offset + index) % size;

}

return index;

}

//insert employee into table

//using Key Offset for collision

public void insertKey(Employee emp) {

int key = emp.getId(); //use emp.id as key

int hashValue = hashFunc(key); //generate hash key

//find available slot for insertion

hashValue = keyOffset(hashValue, key);

table[hashValue] = emp;

}

หลงจากททดสอบดวยโปรแกรมตวเดม ผลลพธทเราไดคอ Slot Id. Name

0 11 Jimmy (collision 3)

1

2 22 Roland

3 33 Once

4

5 55 Again

6

7

8

9 31 De los Santos

10

Probe 1

Probe 2

01

02

03

07

09

08

06

04

05

341

312

253

284

308

222

319

John

Robert

Fred

Jay

Peter

Judy

Jennie

222

312

First insert – No Collision

Second insert – Collision

00

Page 10: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

262

ครงแรกทใส (11, Jimmy) นนเราได index = 0 ครงทสองใส (31, De los Santos) กไมม collision แตหลงจากทใส (22, Roland), (33, Once), และ (55, Again) นนเกด collision สามครงท index = 0 เราจงไดผลลพธดงทเหน ดานลางนคอ code ของ HashTable.java และ TestHashTable.java ทเราไดพดมาทงหมดกอนหนาน

1: /**

2: Hash table with different collision solving methods

3: */

4:

5: public class HashTable {

6: protected Employee[] table;//table contains employees

7: private int size; //size of table

8: private Employee deleted; //indicates deleted employee

9:

10: //default constructor

11: HashTable(int size) {

12: this.size = size;

13: table = new Employee[size];

14: deleted = new Employee(-1, new String("deleted"));

15: }

16:

17: //for output

18: //display collisions on the same slot for each object

19: public String toString() {

20: StringBuffer buffer = new StringBuffer();

21: buffer.append("\n\tSlot\tId.\tName\n");

22: for(int i = 0; i < size; i++) {

23: if(table[i] == null)

24: buffer.append("\t" + i);

25: else if(table[i].getCollision() > 0) {

26: buffer.append("\t" + i + "\t" + table[i].getId());

27: buffer.append("\t" + table[i].getName());

28: buffer.append("\t(collision " +

table[i].getCollision() + ")");

29: }

30: else {

31: buffer.append("\t" + i + "\t" + table[i].getId());

32: buffer.append("\t" + table[i].getName());

33: }

34: buffer.append("\n");

35: }

36: return new String(buffer);

37: }

38:

39: //linear probing

40: private int linearProbe(int hashValue) {

41: //assuming that table is not full

42: while(table[hashValue] != null && table[hashValue].getId() != -1) {

43: table[hashValue].setCollision(); //collisions

44: ++hashValue;

45: hashValue %= size;

46: }

47: return hashValue;

48: }

49:

50: //quadratic probing

51: private int quadraticProbe(int key) {

52: while(table[key] != null && table[key].getId() != -1) {

53: table[key].setCollision();

54: //increment step

55: key += table[key].getCollision() * table[key].getCollision();

56: key %= size;

57: }

58: return key;

59: }

60:

61: //simple hash function

62: public int hashFunc(int key) {

63: return key % size;

64: }

65:

66: //second hash function

Page 11: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

263

67: public int hashAgain(int key) {

68: return 5 - key % 5;

69: }

70:

71: //double hashing using pseudorandom number

72: public int hashTwice(int hashValue, int step) {

73: //assuming that table is not full

74: while(table[hashValue] != null) {

75: table[hashValue].setCollision(); //collisions

76: hashValue += step;

77: hashValue %= size;

78: }

79: return hashValue;

80: }

81:

82: //key-offset

83: private int keyOffset(int index, int key) {

84: int offset;

85: //assuming that table is not full

86: while(table[index] != null) {

87: table[index].setCollision();

88: offset = key / size;

89: index = (offset + index) % size;

90: }

91: return index;

92: }

93:

94: //insert employee into table

95: //using Linear probing for collision

96: public void insertLinear(Employee emp) {

97: int key = emp.getId(); //use emp.id as key

98: int hashValue = hashFunc(key); //generate hash key

99:

100: //find available slot for insertion

101: hashValue = linearProbe(hashValue);

102: table[hashValue] = emp;

103: }

104:

105: //insert employee into table

106: //using Quadratic probing for collision

107: public void insertQuad(Employee emp) {

108: int key = emp.getId(); //use emp.id as key

109: int hashValue = hashFunc(key); //generate hash key

110:

111: //find available slot for insertion

112: hashValue = quadraticProbe(hashValue);

113: table[hashValue] = emp;

114: }

115:

116: //insert employee into table

117: //using Double hashing for collision

118: public void insertTwice(Employee emp) {

119: int key = emp.getId(); //use emp.id as key

120: int hashValue = hashFunc(key); //generate hash key

121: int step = hashAgain(key); //second hash

122:

123: //find available slot for insertion

124: hashValue = hashTwice(hashValue, step);

125: table[hashValue] = emp;

126: }

127:

128: //insert employee into table

129: //using Key Offset for collision

130: public void insertKey(Employee emp) {

131: int key = emp.getId(); //use emp.id as key

132: int hashValue = hashFunc(key); //generate hash key

133:

134: //find available slot for insertion

135: hashValue = keyOffset(hashValue, key);

136: table[hashValue] = emp;

137: }

138:

139: //remove employee from table, assign

140: //-1 to indicate an empty slot

Page 12: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

264

141: public Employee delete(int key) {

142: int hashValue = hashFunc(key); //key to delete

143:

144: while(table[hashValue] != null) {

145: //key is found

146: if(table[hashValue].getId() == key) {

147: Employee temp = table[hashValue];

148: table[hashValue] = deleted; //-1 as empty slot

149: return temp;

150: }

151: ++hashValue;

152: hashValue %= size; //wrap around

153: }

154: return null;

155: }

156:

157: //locate employee

158: public Employee find(int key) {

159: int value = hashFunc(key); //key to find

160: while(table[value] != null) {

161: //key is found

162: if(table[value].getId() == key)

163: return table[value];

164: ++value;

165: value %= size;

166: }

167: return null;

168: }

169: }

Code ของ TestHashTable.java ผอานตองเปลยนการน าขอมลเขาดวยการเรยกใช method ทจ าเปนส าหรบวธนน ๆ ของการแกปญหาเรอง collision (บรรทดท 45)

1: /**

2: Testing hashing routines

3: */

4:

5: import java.util.Scanner;

6: import static java.lang.System.out;

7:

8: class TestHashTable {

9: public static void main(String[] args) {

10: Employee emp;

11: int key, size, n, keyRatio;

12: String name;

13: Scanner sc = new Scanner(System.in);

14:

15: out.print("Enter size of hash table: ");

16: size = sc.nextInt();

17:

18: //create table with random key

19: HashTable empTable = new HashTable(size);

20:

21: //loop until E is pressed

22: while(true) {

23: out.print("Use first letter of (S)how (D)elete

(F)ind (I)nsert or (E)xit: ");

24: char choice = Character.toUpperCase(sc.next().charAt(0));

25: switch(choice) {

26: case 'S': System.out.println(empTable);

27: break;

28: case 'D': out.print("Enter value to delete: ");

29: key = sc.nextInt();

30: empTable.delete(key);

31: break;

32: case 'F': out.print("Enter value to find: ");

33: key = sc.nextInt();

34: emp = empTable.find(key);

35: if(emp == null)

36: out.println("Data not in table");

37: else

38: out.println("Found: " + key);

39: break;

Page 13: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

265

40: case 'I': out.print("Enter id: ");

41: key = sc.nextInt();

42: out.print("Enter name: ");

43: name = sc.next();

44: emp = new Employee(key, name);

45: empTable.insertQuad(emp);

46: break;

47: case 'E': System.exit(0);

48: default : out.println("Invalid value\n");

49: }

50: }

51: }

52: }

10.2.2 กำรใช Linked-list ในกำรแกปญหำเรอง Collision (Separate Chaining)

แทนทเราจะหาต าแหนงใหมเมอเกด collision เรากสราง table ของเราใหมสวนทใชเกบขอมลดวย linked-list ดงแสดงในภาพท 10.6

ภาพท 10.6 Separate Chaining

การใช linked-list เขามาชวยจะท าใหการเกบหรอการคนหาขอมลดเหมอนจะท าใหการแกปญหาของ collision เขาใจไดงายขน แตในเรองของการ implement นนคอนคางจะซบซอนพอด เพราะวาเราจะตองเพมขนตอนของการน าขอมลเขาและออกจาก linked-list ตวอยางตอไปนเปนการใช LinkedList ท Java มใหท าการแกปญหาเรอง collision ดงทไดกลาวมา

1: /**

2: Separate chaining

3: */

4:

5: import java.util.LinkedList;

6:

7: public class SepHashTable {

8: private LinkedList []table;

9: private int size;

10:

11: //default constructor

12: SepHashTable(int size) {

13: this.size = size;

14: table = new LinkedList[size];

15: }

16:

17: //basic hash function

18: private int hash(int key) {

19: return key % size;

20: }

21:

22: //insert object into table

23: public void insert(Employee emp) {

01

02

03

07

09

08

06

04

05

00

311 Jay

421 John

314 Mike

928 Jenny

358 Paul

Page 14: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

266

24: int key = emp.getId();

25: int index = hash(key);

26:

27: //create LinkedList only if it's the first time

28: if(table[index] == null) {

29: table[index] = new LinkedList();

30: }

31: //add Employee into LinkedList

32: table[index].add(emp);

33: }

34:

35: //print contents of table to screen

36: public void print() {

37: for(int i = 0; i < table.length; i++) {

38: if(table[i] != null) {

39: System.out.print(i + "\t");

40: Object emp = table[i].get(0);

41: showFirst((Employee)emp);

42: for(int j = 1; j < table[i].size(); j++) {

43: emp = table[i].get(j);

44: show((Employee)emp);

45: }

46: System.out.println();

47: }

48: else

49: System.out.println(i + "\t(empty)");

50: }

51: }

52:

53: //display first object

54: private void showFirst(Employee e) {

55: System.out.print("(" + e.getId());

56: System.out.print(", " + e.getName() + ")");

57: }

58:

59: //display other objects in LinkedList

60: private void show(Employee e) {

61: System.out.print("->(" + e.getId());

62: System.out.print(", " + e.getName() + ")");

63: }

64: }

SepHashTable.java ยงคงใช Employee เปนขอมลทตองจดเกบและสงส าคญทเราตองท ากคอการก าหนดให table เปน array ทใชเกบ LinkedList และขอมลทอยใน LinkedList เปน Employee การจดเกบทวากไมยากเทาไร ดงทแสดงใหดจาก insert() public void insert(Employee emp) {

int key = emp.getId();

int index = hash(key);

//create LinkedList only if it's the first time

if(table[index] == null)

table[index] = new LinkedList();

//add Employee into LinkedList

table[index].add(emp);

}

หลงจากทเราได index จาก hash() แลวเรากตรวจสอบดวาเราไดสราง LinkedList ณ ต าแหนง index นแลวหรอยง ถายงเรากสรางแตถาสรางไวกอนหนานแลวเรากท าการเพมขอมลเขาส LinkedList ดวยประโยค table[index].add(emp);

เราไดท าการทดสอบ code ทเขยนขนดวยโปรแกรม TestSepHashTable.java ทเราได ดดแปลงมาจาก TestHashTable.java ทเราเขยนขนมากอนหนาน

1: /**

2: Testing Separate chaining Hash table

3: */

4:

Page 15: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

267

5: import java.util.Scanner;

6: import java.io.IOException;

7: import static java.lang.System.out;

8:

9: class TestSepHashTable {

10: public static void main(String[] args) throws IOException {

11: Employee emp;

12: int key, size;

13: String name;

14: Scanner sc = new Scanner(System.in);

15:

16: out.print("Enter size of hash table: ");

17: size = sc.nextInt();

18: SepHashTable empTable = new SepHashTable(size);

19: //loop until E is pressed

20: while(true) {

21: out.print("Use first letter of (S)how (I)nsert or (E)xit: ");

22: char choice = Character.toUpperCase(sc.next().charAt(0));

23: switch(choice) {

24: case 'I': out.print("Enter id: ");

25: key = sc.nextInt();

26: out.print("Enter name: ");

27: name = sc.next();

28: emp = new Employee(key, name);

29: empTable.insert(emp);

30: break;

31: case 'E': System.exit(0);

32: case 'S': empTable.print();

33: break;

34: default : out.println("Invalid value");

35: }

36: }

37: }

38: }

เราไดผลลพธ (หลงจากท run ไปไดระยะหนง) ดงน 0 (10, paul)->(20, jay)

1 (empty)

2 (empty)

3 (23, ray)

4 (14, rock)->(44, joop)->(54, salem)

5 (empty)

6 (empty)

7 (empty)

8 (empty)

9 (empty)

เราไมไดเขยน code ส าหรบการคนหา หรอ การลบ ใหด ซ งเราตงใจวาจะใหเปนแบบฝกหดส าหรบผอานตอไป โปรแกรมตวอยางทเราไดแสดงใหดทงหมด ใชการหาต าแหนงดวยการใช int เปนตวก าหนด แตงานหลาย ๆ งานทเปนอยจรงโดยทวไปตองใช ตวอกษรเปนตวก าหนดดงทเราไดกลาวไวตอนเรมตน ของบทน ซ งถาเราตองการเรากตองท าการเปลยน hash() ใหมการใชองคประกอบของตวอกษรเหลานนมาเปนตวก าหนด ดงทจะแสดงใหดจากตวอยางตอไปน 10.3 กำรหำคำ index จำก String ในการหา index จาก String นนมวธการหลายวธทเราสามารถท าได วธการทแสดงใหดตอไปนเปนวธการงาย ๆ ทแปลงตวอกษรใหเปนตวเลขทเราสามารถน ามาใชเปน index ได //get hash value from name

private int hash(String name) {

int h = 0;

for(int i = 0; i < name.length(); i++)

h = (M * h + name.charAt(i)) % size;

return h;

}

Page 16: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

268

เราเพยงแตเอาคาของตวอกษรทกตวใน String มารวมกน หลงจากทเราคณตวเลขทเราได ก าหนดไว (ในทนเราใช M = 127) สาเหตทเราใชคา M = 127 กเพราะวา char สามารถใช

จ านวนของ bit ในการเกบตวอกษรในรปแบบของ ASCII เพยงแค 7 bit (27 = 128 [0...127]) ลองดตวอยางของค าวา Peter คา ASCII ของ 'P' = 80, 'e' = 101, 't' = 116, 'r' = 114 เพราะฉะนนคาของ h ใน for/loop กจะเปน h0 = (127 * 0 + 80) % 11 = 3 h1 = (127 * 3 + 101) % 11 = 9 h2 = (127 * 9 + 116) % 11 = 5

h3 = (127 * 5 + 101) % 11 = 10 h4 = (127 * 10 + 114) % 11 = 9 hash ของเรากจะไดคา index เทากบ 9 วธการนเปนวธการงาย ๆ และเปนการหาคา index ทไมซบซอนมากมายเทาใดนก แตถาน าไปใชงานจรงกคงตองหาวธการหาคา index ทรดกมกวา เนองจากวาการค านวณดวย % นน

เสยคา overhead สงมาก เราไดเปลยนแปลงไฟล Employee.java ใหมเพอใหรองรบการหาคา index ดวยการใช String ดงน พรอมทงไดสราง id ของ employee ใหมดวยการน าเอาชอสองตวแรก กบตวอกษรอกสามตวทเราไดสมข นมาเอง method generateId() เปนผท าหนาทดงกลาว method อน ๆ ท เหลออย เราเอาไวใชส าหรบการก าหนดจ านวนครงของ collision ทเกดข น เพอใชในการแสดงผล

1: /**

2: Employee using string in name to create index

3: */

4:

5: import java.util.Locale;

6:

7: public class Employee {

8: private String name;

9: private String id;

10: private int collision;

11: private int pIndex;

12: private char []alpha = {'a', 'b', 'c', 'd', 'e', 'f', 'g',

13: 'h', 'i', 'j', 'k', 'l', 'm', 'n', 14: 'o', 'p', 'q', 'r', 's', 't', 'u', 15: 'v', 'w', 'x', 'y', 'z'}; 16:

17: //default constructor

18: Employee(String name) {

19: this.name = name;

20: this.id = generateId();

21: }

22:

23: //return this name

24: public String getName() {

25: return name;

26: }

27:

28: //generate 5 character Id for this employee

29: private String generateId() {

30: id = "";

31: //get first 2 characters from name

32: id = name.substring(0, 2);

33: //add 3 more random characters

34: for(int i = 0; i < 3; i++) {

35: id += alpha[((int)(Math.random() * 25))];

36: }

37: //convert them to uppercase

38: id = id.toUpperCase(Locale.US);

39: return id;

Page 17: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

269

40: }

41:

42: //return this id

43: public String getId() {

44: return id;

45: }

46:

47: //set collision

48: //for displaying purpose only

49: public void setCollision() {

50: collision++;

51: }

52:

53: //return this collision of this

54: //employee's slot

55: public int getCollision() {

56: return collision;

57: }

58:

59: public int getPindex() {

60: return pIndex;

61: }

62:

63: public void setPindex(int index) {

64: pIndex = index;

65: }

66:

67: //display employee's info

68: public String toString() {

69: StringBuffer buffer = new StringBuffer();

70: buffer.append(name + "\n");

71: return new String(buffer);

72: }

73: }

ส าหรบไฟล HashTable.java นนม code ดงน 1: /**

2: Hash table using string as index

3: */

4:

5: class HashTable {

6: private Employee[] table;

7: private int size; //table's size

8: private int M = 127; //multiplier

9:

10: //allocate space for table

11: HashTable(int size) {

12: table = new Employee[size];

13: this.size = size;

14: }

15:

16: //get hash value from name

17: private int hash(String name) {

18: int h = 0;

19: for(int i = 0; i < name.length(); i++)

20: h = (M * h + name.charAt(i)) % size;

21: return h;

22: }

23:

24: //insert into table, no error checking

25: public void insert(Employee emp) {

26: int index = hash(emp.getName());

27: emp.setPindex(index);

28: index = linearProbe(index);

29: table[index] = emp;

30: }

31:

32: //solving collision with linear probe

33: private int linearProbe(int index) {

34: while(table[index] != null) {

35: table[index].setCollision();

36: index++;

Page 18: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

270

37: index %= size;

38: }

39: return index;

40: }

41:

42: //display collisions on the same slot for each object

43: public String toString() {

44: StringBuffer buffer = new StringBuffer();

45: buffer.append("\n\tSlot\tKey\tId.\tName\n");

46: for(int i = 0; i < size; i++) {

47: if(table[i] == null)

48: buffer.append("\t" + i);

49: else if(table[i].getCollision() > 0) {

50: buffer.append("\t" + i + "\t" +

table[i].getPindex() + "\t" + table[i].getId());

51: buffer.append("\t" + table[i].getName());

52: buffer.append("\t(collision " +

table[i].getCollision() + ")");

53: }

54: else {

55: buffer.append("\t" + i + "\t" +

table[i].getPindex() + "\t" + table[i].getId());

56: buffer.append("\t" + table[i].getName());

57: }

58: buffer.append("\n");

59: }

60: return new String(buffer);

61: }

62: }

สวนโปรแกรมทดสอบ TestHashString.java ม code ดงน

1: /**

2: Test String hash

3: */

4:

5: class TestHashString {

6: public static void main(String[] args) {

7: Employee []emp;

8: int size = 11;

9:

10: HashTable table = new HashTable(size);

11: emp = new Employee[size];

12:

13: //create 7 employees

14: emp[0] = new Employee("James Callahan");

15: emp[1] = new Employee("John Smith");

16: emp[2] = new Employee("Phillips Khan");

17: emp[3] = new Employee("Mark Twain");

18: emp[4] = new Employee("Peter Walker");

19: emp[5] = new Employee("Michael Kawakami");

20: emp[6] = new Employee("Andrew Roll");

21:

22: //insert employees

23: for(int i = 0; i < 7; i++)

24: table.insert(emp[i]);

25:

26: //display table

27: System.out.println(table);

28: }

29: }

ผลลพธของการ run คอ

Slot Key Id. Name

0 10 PEETN Peter Walker

1 1 MAAKM Mark Twain

2 2 JOURI John Smith (collision 1)

3 2 MIEEW Michael Kawakami

4

5 5 JAOTK James Callahan

Page 19: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

271

6

7 7 ANPPU Andrew Roll

8

9

10 10 PHALT Phillips Khan (collision 1)

ผลลพธทไดแสดงต าแหนงของ Table ทเกบขอมล แสดงคาของ key ทเกดจาก hash function แสดงรหสทสรางข น และแสดงชอของ employee นน ๆ 10.4 กำรใช Hash table ท Java มให Java ม class Hashtable ทเราสามารถเรยกใชเพอท าการน าขอมลเขาในรปแบบของ (key, value) ดงเชนทเราไดท ามากอนหนาน แต Java ไมยอมใหมการน าเขาขอมลทม key เหมอนกน ลองดโปรแกรมตวอยางดานลางน

1: /**

2: Simple Java's hash

3: */

4:

5: import java.util.Hashtable;

6:

7: public class JavaHashTable {

8: public static void main(String[] args) {

9: Hashtable t = new Hashtable();

10: for(int i = 0; i < 5; i++) {

11: t.put(new Integer(i), new Integer(i));

12: }

13: t.put(new Integer(5), "9");

14: t.put(new Integer(5), "10");

15: t.put(new Integer(5), "11");

16: for(int i = 0; i < t.size(); i++) {

17: System.out.println(t.get(new Integer(i)));

18: }

19: }

20: }

หลงจากทเราสราง Hashtable t ดวยประโยค Hashtable t = new Hashtable() แลวเรากน าเขาขอมล 5 ตวโดยก าหนดให key เปน object จาก class Integer และคาทเกบกเปน Integer ตวเดยวกน หลงจากนนเราน าเขาขอมลอก 3 ตวดวย key ตวเดยวกนคอ Integer(5) แตคาทเกบตางกน ผลลพธทเราไดจากการ run โปรแกรม HashTable.java คอ 0

1

2

3

4

11

ซงแสดงใหเหนวา Java จะเกบขอมลทบขอมลเดมถา key ของขอมลเหลานเหมอนกน เพราะฉะนนถาเราตองการให Hashtable เกบขอมลใหถกตองเราตองบงคบใหขอมลแตละตวทจะเกบใน Hashtable ม key ทแตกตางกน โปรแกรมตวอยางตอไปนเปนโปรแกรมทเรยกใช class HashMap แทน class Hashtable ทงนเพราะ HashMap ท างานไดไวกวา Hashtable (ยงมขอแตกตางระหวาง class ทงสองอก ผอานสามารถหาขอมลเพมเตมไดจากเวบไซดของ Sun – แตโดยรวม ๆ แลวท างานคลายกน) เนองจากวาเราตองรบประกนวา key ทกตวทสรางข นจะตองเปน key ทมความเปนเอกภาพ ดงนนเราจงจ าเปนทจะตองท าการ override method equals() และ method hashCode() ทงนกเพราะวา HashMap เมอท าการน าขอมลเขา (ดวยการเรยกใช put()) จะค านวณหาต าแหนงดวยคาของ hashCode ทเกดข นดวยการเปรยบเทยบกบขอมลทมอยกอนแลว (เรยกใช equals()) ถาไมม key นอยกเกบขอมลน (key/value) ไว แตถาม key นอยแลวกจะเขยนทบขอมลเดม ซงเปนส งทเราไมตองการ

Page 20: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

272

เราไดออกแบบ class Employee ของเราขนใหมเพอรองรบการน าขอมลเขาทม key ทเปนเอกภาพดงน

1: /**

2: Employee class for Java's hash

3: */

4:

5: public class Employee {

6: private String name;

7: private int id;

8: private String position;

9:

10: //default constructor

11: Employee(int id, String name, String position) {

12: this.id = id;

13: this.name = name;

14: this.position = position;

15: }

16:

17: //generate unique hashcode using name and id

18: public int hashCode() {

19: return name.hashCode() * id;

20: }

21:

22: //override equals() to make distinction

23: public boolean equals(Object o) {

24: return (o instanceof Employee)

25: && name.equals(((Employee)o).name)

26: && id == ((Employee)o).id;

27: }

28:

29: //return employee's id

30: public int getId() {

31: return id;

32: }

33:

34: //return employee's name

35: public String getName() {

36: return name;

37: }

38:

39: //return employee's position

40: public String getPosition() {

41: return position;

42: }

43:

44: //display using format: (name, id, hashCode)

45: public String toString() {

46: StringBuffer buf = new StringBuffer();

47: buf.append("\n(" + name + ", " + id + ", " + hashCode() + ")");

48: return new String(buf);

49: }

50: }

ใน hashCode() นนส งทเราท ากคอการน าเอาคา hashCode ของ String ทใชเปน key ในการเขาหา มาคณกบคาของ id ซงเปน int เพอท าให key ของ String ตวนมคาทตางกน เพอเปนการปองกนการ hash ลงสทเดยวกน ในสวนของ equals() นนเราท าการตรวจสอบความเหมอนกนของ object สองตวจาก class Employee โดยเราจะตรวจสอบทง name และ id ของ object ทงสองวาเหมอนกนหรอไม ในสวนของโปรแกรมทดสอบนนเรากท าอยางงาย ๆ เพอให เหนโครงสรางภายในของ HashMap วาเกบขอมลอยางไร (จรง ๆ แลวขอมลทเราเหนมาจากการ overload toString() ใน class Employee)

1: /**

2: Test Java's hash

3: */

4:

5: import java.util.Collection;

6: import java.util.HashMap;

7:

8: public class HashTable {

Page 21: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

273

9: //use HashMap to store employee's data

10: //generate unique hashcode using name and id

11: //where name can be duplicated strings but id must be unique

12: public static void main(String[] args) {

13: HashMap<Employee, String> map = new HashMap<Employee, String>();

14: Employee []emp = new Employee[7];

15: for(int i = 0; i < emp.length; i++) {

16: //create employees named "Joe", id = i, and

17: //position = Programmer(i+1)

18: emp[i] = new Employee(i, "Joe", "Programmer" + (i+1));

19: //store employee in HashMap

20: map.put(emp[i], emp[i].getPosition());

21: }

22: //print information stored in map

23: System.out.println(map);

24: //print values stored at given keys (emp[i])

25: for(int i = 0; i < emp.length; i++) {

26: System.out.println("Value of emp[" + i + "]: " +

map.get(emp[i]));

27: }

28: //remove one employee from map

29: //display values via Collection

30: map.remove(emp[4]);

31: Collection c = map.values();

32: System.out.println(c);

33: }

34: }

หลงจากทสราง map แลวเราก าหนดให emp เกบ object จ านวน 7 ตวโดยใหทกตวม key ทเหมอนกนคอ "FEC" แตม id ทตางกน และคาทอยในต าแหนงทเกดจาก key และ id นมคาเปน programmer1 ถง programmer7 หลงจากทเราสราง object เสรจแลวเรากแสดงขอมลภายใน map พรอมกบแสดงคา ณ ต าแหนงนน ๆ ดวย และเพอใหเหนการจดการกบขอมลอยางแทจรงเราจงลบ object ณ ต าแหนงท 4 ออก พรอมกบแสดงขอมลทเหลออกครงหนง ผลลพธทเราไดจากการ run โปรแกรมคอ {

(Joe, 4, 298624)=Programmer5,

(Joe, 6, 447936)=Programmer7,

(Joe, 2, 149312)=Programmer3,

(Joe, 3, 223968)=Programmer4,

(Joe, 1, 74656)=Programmer2,

(Joe, 5, 373280)=Programmer6,

(Joe, 0, 0)=Programmer1}

Value of emp[0]: Programmer1

Value of emp[1]: Programmer2

Value of emp[2]: Programmer3

Value of emp[3]: Programmer4

Value of emp[4]: Programmer5

Value of emp[5]: Programmer6

Value of emp[6]: Programmer7

[Programmer7, Programmer3, Programmer4, Programmer2, Programmer6, Programmer1]

ผอานจะเหนวาในการแสดงผลดวยค าสง System.out.println(map) นน Java จะแสดงคาทเปนค (key/value) โดยทคา key จะอยในวงเลบ เชน (Joe, 5, 373280) สวนคา value นนจะอยหลงเครองหมายเทากบ เชน = Programmer6 และเนองจากวาเราไดท าการ overload method toString() เพอใหแสดงผลทอยในรปแบบของ (name, id, hashCode) ซงถาเราไม overload method toString() เรากจะไดผลลพธดงน {Employee@54cf4=Programmer6, Employee@32e2c=Programmer4,

Employee@65c58=Programmer7, Employee@21ec8=Programmer3,

Employee@10f64=Programmer2, Employee@43d90=Programmer5,

Employee@0=Programmer1}

ซงเปนคาจรง ๆ ของ (key/value) ท Java ไดเกบไวใน map ทเราไดสรางขน ผอานจะสงเกตไดวาคา key ท Java เกบไวเปน address ของ object ทเกดจาก class Employee นน นนเอง และหลงจากทเราแสดงผลของ object ทอยใน map แลวเรากเรยก method get() ท าการแสดงผลของคาทเกบอย ณ key (emp[i]) นน ๆ หลงจากทเราท าการลบ key ออกหนงตวเราก

Page 22: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

274

แสดงคาของ key อกครงหนงดวยการเรยกใช method values() ซง values() ทวาจะสงคาผานทาง Collection เพราะฉะนน ผลลพธทเราไดจากการเรยก Collection c = map.values();

System.out.println(c);

จงมคาเปน [Programmer6, Programmer4, Programmer7, Programmer3, Programmer2, Programmer1]

โปรแกรมนเปนเพยงแตตวอยางแสดงใหเหนถงการใช HashMap ในการจดการกบขอมลอยางงาย ๆ เทานน ยงมวธการอน ๆ อก ทงนกข นอยกบลกษณะของงาน หรอโปรแกรมนน ๆ ผอานอาจตองใชเวลาสกหนอยในการท าความเขาใจกบการท างานของ HashMap 10.5 Dynamic hashing การ hash ทเราไดแสดงใหดมขดจ ากดในเรองของจ านวนของขอมลทสามารถเกบได ดงทเราไดพดกอนหนานวาเราสามารถใชการขยายขนาดของ array มาชวยได โปรแกรม DynamicHashTable.java เปนโปรแกรมตวอยางการขยายขนาดของ table ทใช Linear probing เปนกระบวนการแกปญหาของ index ทซ ากน และเพอเปนการประหยดเนอทเราจงตดเอา method toString() และ hashFunc() ออก

1: /**

2: Dynamic hash table

3: */

4:

5: class DynamicHashTable {

6: private Employee []table;

7: private int size; //size of table

8: private int count; //item counted

9:

10: //default constructor

11: DynamicHashTable(int size) {

12: this.size = size;

13: count = 0;

14: table = new Employee[size];

15: }

16:

/** toString() และ hashFunc() ไดถกตดออก – ดจากโปรแกรมกอนหนาน **/

43:

44: //insert employee into table

45: //using Linear probing for collision

46: public void insert(Employee emp) {

47: int key = emp.getId(); //use emp.id as key

48: int index = hashFunc(key); //generate hash key

49:

50: //search for suitable location

51: while(table[index] != null) {

52: table[index].setCollision();

53: ++index;

54: index %= size;

55: }

56: table[index] = emp;

57:

58: //if exceeds half the size of table, double it

59: if(count++ >= size/2)

60: expand();

61: }

62:

63: //expand table size and re-hash

64: private void expand() {

65: //create new table and points it to

66: //the working table

67: Employee []tab = table;

68: count = 0;

69: size += size; //double the size

70: table = new Employee[size];

Page 23: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

275

71: //re-hashing

72: for(int i = 0; i < size/2; i++)

73: if(tab[i] != null)

74: insert(tab[i]);

75: }

76: }

Method ทส าคญส าหรบการน าขอมลเขาคอ insert() และ expand() โดยเฉพาะ method expand() นนเราก าหนดใหถกเรยกจาก method insert() เมอขนาดของจ านวนของขอมลทน าเขาเทากนหรอมากกวาครงหนงของขนาดของ table และจะท าการน าขอมลเขาส table ใหมอกครงหนง (re-hash) เพราะฉะนนต าแนงของขอมลจาก table ตวเกากอนการขยายขนาดอาจจะไมเปนทเดยวกน ทงนเพราะเราใชขนาดของ table เปนตวชวยในการหาต าแหนงของขอมล ผลลพธทไดจากการทดสอบดวยขนาดของ table เทากบ 4 Enter name: jim

Use first letter of (S)how (I)nsert or (E)xit: s

Slot Id. Name

0 56 jim

1 21 dan

2

3

Use first letter of (S)how (I)nsert or (E)xit: i

Enter id: 33

Enter name: pete

Use first letter of (S)how (I)nsert or (E)xit: s

Slot Id. Name

0 56 jim

1 33 pete

2

3

4

5 21 dan (collision 1)

6

7

การขยายขนาดเปนกระบวนการทมคาใชจาย (overhead) คอนขางสง เนองจากสาเหตทกลาวมาแลว คอ การ re-hash แตเราอาจมองในอกมมหนงไดวา การขยายขนาดไมไดเกดข นบอยนก ดงนนคาใชจายทเกดข นเมอเปรยบเทยบกบการสราง table กเปนเพยงสวนนอยนดเทานน สรป ในบทนเราไดท าการศกษาถงวธการของการใช Hash table ในการจดเกบขอมลรวมไปถงการแกปญหาของ collision ทเกดข นจากการ hash ลง ณ ต าแหนงเดยวกนดวยวธการตาง ๆ เราได เนนถงการก าหนดขนาดของ hash table ทเหมาะสมวาควรจะเปนเลขทเปน prime (ถงแมวาตวอยางหลาย ๆ ตวของการ run โปรแกรมเราใชขนาดทไมใชเลข prime) โดยสรปแลวเราได พดถง Hash function การน าขอมลเขาส table ดวย index ทเกดจาก hash function การแกปญหาเรอง collision ดวยเทคนควธการตาง ๆ การหาคาของ index จาก String การใช Hashtable และ HashMap Dynamic hashing

Page 24: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

276

แบบฝกหด 1. จงเขยนโปรแกรมทใช linear probe ในการแกปญหาเรอง collision ถา table มขอมล

ตอไปนอย และขนาดของ table เทากบ 19

224562 137456 214562 140145 214567 162145 144467 199645 234534

พรอมทงหาจ านวนของ collision วามทงหมดกครง

2. จากขอก าหนดทใหในขอ 1 จงเขยนโปรแกรมดวยการใช quadratic probe

3. จากขอก าหนดทใหในขอ 1 จงเขยนโปรแกรมดวยการใช hash function 2 ตวโดยท hash

function ตวทสองตองไมสราง index เหมอนกบ hash function ตวแรก 4. จากขอก าหนดทใหในขอ 1 จงเขยนโปรแกรมดวยการใช pseudorandom 5. จากขอท 1 จงเขยนโปรแกรมดวยการใช key-offset

6. จากขอก าหนดทใหในขอ 1 จงเขยนโปรแกรมดวยการใช Linked-list แบบทเปนการน าเขา

ทจดเรยง (Ordered Linked-List) 7. จงเขยน method find(int key) และ method delete(int key) ทท าหนาทในการคนหา

ขอมล และลบขอมลจาก key ทก าหนดให ออกจาก Hash table ทใช Separate chaining ในการแกปญหาของ collision

8. จงเขยนโปรแกรมทแสดงการใช class Hashtable ของ Java ในการเกบขอมลของ

นกศกษา โดยก าหนดใหใช ชอ และ รหสประจ าตวเปน key และใช ทอยของนกศกษาเปนคา ณ ต าแหนงนน

9. จงเขยนโปรแกรมทใช hashing algorithm เพอสรางรายการสนคาคงคลงทประกอบไปดวย

part number และ quantity หลงจากทสราง hashed list แลวใหเขยน menu-driven user interface อยางงายเพอรองรบการท างานทจะเกดข นจาก user ดงน

9.1. คนหา part ทมอยใน hashed list พรอมทงจ านวนทมอย 9.2. แสดงผลของรายการ part และจ านวนทงหมดทมอย 9.3. ลบรายการ part ทก าหนดจาก user 9.4. เพมรายการเขาส hashed list

ขอมลทมอยในไฟลสนคามดงน

Part Number Quantity

112 12

130 30

156 56

173 17

197 19

150 50

166 66

113 13

123 12

143 14

167 16

189 18

193 19

117 11

176 76

Page 25: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

บทท 10 Hash Table

277

โปรแกรมควรทอสอบดวยขอมลทมความหลากหลาย โดยเฉพาะขอมลทก าหนดใหน คนหา 112 คนหา 126 คนหา 173 10. จงเขยนโปรแกรมเพอตรวจสอบความรวดเรวของ Hash algorithm ตาง ๆ ดวยขอมลทเกด

จากการใช random number generator จ านวน 1000 ตว 10000 ตว และ 100000 ตว 11. จงดดแปลงโปรแกรมตวอยางในเรองของการหาคา index จาก String ใหสามารถสราง id

number จาก ชอ และนามสกลของ employee โดยใหเปลยน class Employee ใหมเพอ

รองรบการเกบทงชอ และนามสกลทแยกกน และ id ทสรางขนจะตองประกอบไปดวยอกษรรสองตวจากชอ และอกษรอกสองตวจาก นามสกล ใหเขยนโปรแกรมทดสอบ

12. function hash() ทเราสรางขนในการหาคา index จาก String นนจะมปญหาถาเราเปลยน

การค านวณใหเปนดงน

private int hash(String name) {

int h = 0;

for(int i = 0; i < name.length(); i++)

h = M * h + name.charAt(i);

h %= size;

return h;

}

ปญหาทวากคอ เราอาจไดคา index ทมคานอยกวาศนย ซงจะท าใหโปรแกรมของเราลมเหลว จงหาวธการแกปญหาดงกลาว ถาเรายงคงการท างานในลกษณะนอย (ท าการ % หลงจากการหาคาของ h เรยบรอยแลว

13. จากโจทยในขอแปด จงเขยนโปรแกรมทดสอบดวยคา M ตาง ๆ ทคดวาท าใหการ

ค านวณหาคา index ดข น (collision นอยลง หรอไมมเลย) 14. คา index ทหาไดจาก hash function ทใช String เปนตวก าหนดขนอยกบตวอกษรท

ประกอบอยใน String นน ๆ ดงนนในบางโอกาสเรากอาจจะไดคา index ทซ ากน (ถา

String เหมอนกน) จงหาวธการในการทจะไดมาซง index ทไมซ ากน

Page 26: Hash Table เป็นโครงสรา้งชนิดหนึ่งที่ช่วยใหก้ารนาขอ้มูลเขา้ ...sci.feu.ac.th/faa/dsa/bookPDFs/chap10-HashTable.pdf ·

Hash Table บทท 10

278