-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRobinHoodHashing.java
244 lines (224 loc) · 5.76 KB
/
RobinHoodHashing.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package aab180004;
import java.util.Comparator;
/**
* @author Mythri Thippareddy
* The class implements Robinhood Hashing
* @param <K>
* : K is type of elements inserted into the hashtable
*/
public class RobinHoodHashing<K extends Comparable<? super K>> {
private int size;// Number of elements in the hashTable
private int capacity;// The size of the hashTable
private Entry[] hashTable; // HashTable of type Entry class
private int maxDisplacement = 0; // Keep track of the maximum displacement of the elements in the hash table
static final int DEFAULT_INITIAL_CAPACITY = 16;
static final float DEFAULT_LOAD_FACTOR = (float) 0.6;
private float loadFactor;
/**
* @author Mythri Thippareddy
*
* @param <K>
* : K is type of elements inserted into the hashtable
*/
static class Entry<K extends Comparable<? super K>> {
K key;
boolean isDeleted;
int displacement;
public Entry(K key) {
this.key = key; // The key element inserted
this.isDeleted = false; // Whether an element is deleted or not
this.displacement = 0; // Displacement by the element which it's original index
}
}
/**
* Constructor where the hastable is initialized
*/
public RobinHoodHashing() {
this.capacity = DEFAULT_INITIAL_CAPACITY;
this.loadFactor = DEFAULT_LOAD_FACTOR;
this.hashTable = new Entry[this.capacity];
}
/**
* @param h
* : value calculated for indexFor Method
* @return
*/
private static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* @param h
* : from the hash method
* @param length
* : the capacity of the hashTable
* @return
*/
private static int indexFor(int h, int length) {
return h & (length - 1);
}
/**
* @param x
* : The element to be hashed
* @return
*/
private int hashCode(K x) {
return indexFor(hash(x.hashCode()), this.capacity);
}
/**
* @param x:
* if x exists in the hashTable, return true, else false
* @return
*/
public boolean contains(K x) {
if (find(x) == null) {
return false;
}
return true;
}
/**
* @param x
* : If x exists in hashtable, return the index. Else return null
* @return
*/
private Integer find(K x) {
int hx = hashCode(x);
int d = 0;
while (this.hashTable[hx] != null) {
if (this.hashTable[hx].displacement >= d || d < this.maxDisplacement) {
if ((this.hashTable[hx].isDeleted == false && ((K) this.hashTable[hx].key).compareTo((K) x) == 0)) {
return hx;
} else {
hx = (hx + 1) % this.capacity;
d = d + 1;
}
} else
break;
}
return null;
}
/**
* @param x
* Return true if x exists and is marked as delete. Else return false
* @return
*/
public boolean remove(K x) {
Integer hx = find(x);
if (hx == null) {
return false;
} else {
this.hashTable[hx].isDeleted = true;
return true;
}
}
/**
* Rehashing the hashTable when the loadfactor reaches the limit
*/
private void rehash() {
Entry[] oldHashTable = this.hashTable;
this.capacity = this.capacity * 2;
this.hashTable = new Entry[this.capacity];
this.size = 0;
this.maxDisplacement = 0;
for (Entry eachEntry : oldHashTable) {
if (eachEntry != null) {
if (eachEntry.isDeleted == false) {
this.addElement((K) eachEntry.key);
}
}
}
return;
}
/**
* @return the current loadFactor
*/
private float calculateLoadFactor() {
return (float) this.size / this.capacity;
}
/**
* @return true if the table needs to be rehashed
*/
private boolean needToRehash() {
if (this.calculateLoadFactor() >= this.loadFactor) {
return true;
}
return false;
}
/**
* @return number of unique keys in the hashtable
*/
public int numberOfKeys() {
int numberOfKey = 0;
for (Entry eachEntry : this.hashTable) {
if (eachEntry != null) {
if (eachEntry.isDeleted == false) {
numberOfKey++;
}
}
}
return numberOfKey;
}
/**
* @param x:
* add element x
*/
private void addElement(K x) {
int hx = hashCode(x);
int displacementCount = 0;
while (true) {
if (this.hashTable[hx] == null) {
this.hashTable[hx] = new Entry(x);
this.hashTable[hx].displacement = displacementCount;
if (displacementCount > this.maxDisplacement) {
this.maxDisplacement = displacementCount;
}
this.size++;
if (this.needToRehash()) {
rehash();
}
break;
} else if (this.hashTable[hx].isDeleted == true) {
this.hashTable[hx].key = x;
this.hashTable[hx].displacement = displacementCount;
this.hashTable[hx].isDeleted = false;
if (displacementCount > this.maxDisplacement) {
this.maxDisplacement = displacementCount;
}
break;
} else if (this.hashTable[hx].displacement < displacementCount) {
K xOld = (K) this.hashTable[hx].key;
this.hashTable[hx].key = x;
this.hashTable[hx].displacement = displacementCount;
if (displacementCount > this.maxDisplacement) {
this.maxDisplacement = displacementCount;
}
x = xOld;
}
displacementCount++;
hx = (hx + 1) % this.capacity;
}
}
/**
* @param x
* @return
*/
public boolean add(K x) {
if (this.contains(x)) {
return false;
} else {
addElement(x);
return true;
}
}
/**
* @param arr : Return the number of unique elements
* @return
*/
static <T extends Comparable<? super T>> int distinctElements(T[] arr) {
RobinHoodHashing<T> hashMap = new RobinHoodHashing<T>();
for (int i = 0; i < arr.length; i++) {
hashMap.add((T) arr[i]);
}
return hashMap.numberOfKeys();
}
}