-
-
Notifications
You must be signed in to change notification settings - Fork 610
/
Copy pathHashedArrayTree.java
121 lines (96 loc) · 3.08 KB
/
HashedArrayTree.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
package ds;
/**
* Created by sherxon on 5/23/17.
*/
/**
* This is the implementation of Hashed Array Tree (HAT) data structure that is the same as Dynamic
* Array (ArrayList in Java) in terms of time complexity, however, it wastes O(SQRT(N)) extra space
* when increasing capacity.
*
* Although name implies hashing, HAT does not use any hash.
*
* Initially, we start with base array that stores only references to leaf arrays whose size is
* the same as base array. Every time current leaf array is full we create new leaf array and start
* to insert data into newly created leaf array.
*
* When increasing capacity, we double size of base array and leaf create leaf arrays with doubles
* size and copy entries from previous HAT. In this way, only quarter of newly created HAT is full
* and other leaf arrays will be null.
*/
public class HashedArrayTree {
private Object[][] data; // base array, used to store references to leaf arrays
private int size;
private int iPointer = -1;
private int jPointer = 17;
public HashedArrayTree() {
this.data = new Object[16][]; // should be power of 2, all leaf arrays are not initialized
}
public void add(Integer elem) {
ensureCapacity();
Object[] leaf = getLeaf();
leaf[jPointer++] = elem;
size++;
}
/**
* this method checks capacity and extend it if needed.
* all data copied to new storage and only one empty leaf will be available to insert
* Extra wasted space is equal to 2*SQRT(n);
*/
private void ensureCapacity() {
if (iPointer + 1 < data.length || jPointer < data[iPointer].length) {
return;
}
int prevLength = data.length;
Object[][] newData = new Object[prevLength * 2][];
iPointer = prevLength / 2;
for (int i = 0; i < iPointer; i++) {
newData[i] = new Object[prevLength * 2];
for (int j = 0; j < newData[i].length; j++) {
newData[i][j] = data[i % prevLength + j / prevLength][j % prevLength];
}
}
jPointer = 0;
newData[iPointer] = new Object[prevLength * 2];
this.data = newData;
}
/**
* helper method to see capacity and number of entries in HAT
*/
public void printStats() {
int busy = 0;
int cap = 0;
for (int i = 0; i < data.length; i++) {
if (data[i] != null) {
for (int j = 0; j < data[i].length; j++) {
if (data[i][j] != null) {
busy++;
}
cap++;
}
}
}
System.out.println("Size: " + size + "; Capacity: " + cap + "; Busy: " + busy);
}
public Object get(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException();
}
int iiPointer = index / data.length;
int jjPointer = index % data.length;
return data[iiPointer][jjPointer];
}
public int size() {
return size;
}
/**
* returns available leaf to insert data.
* if current leaf is already full,creates new leaf and returns it
*/
private Object[] getLeaf() {
if (jPointer >= data.length) {
data[++iPointer] = new Object[data.length];
jPointer = 0;
}
return data[iPointer];
}
}