Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
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 บทท 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
บทท 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
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 ทเกดข น (เพอแสดงผล) ดวยประโยค
บทท 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 ทงหมดเราไดจดแสดงไวในตอนทายของเรอง
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
บทท 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
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 อกเรากเรมกระบวนการใหมอกครง
บทท 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
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
บทท 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
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;
บทท 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
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:
บทท 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;
}
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;
บทท 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++;
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
บทท 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 นอยแลวกจะเขยนทบขอมลเดม ซงเปนส งทเราไมตองการ
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 {
บทท 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 ออกหนงตวเราก
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];
บทท 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
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
บทท 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 ทไมซ ากน
Hash Table บทท 10
278