Skip to content

Commit 8b634d5

Browse files
author
Justin Wetherell
committed
Added some timing tests of the spatial data structures
1 parent 6f823dc commit 8b634d5

File tree

6 files changed

+487
-131
lines changed

6 files changed

+487
-131
lines changed

src/com/jwetherell/algorithms/data_structures/KdTree.java

+57-44
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ public class KdTree<T extends KdTree.XYZPoint> {
2626
private KdNode root = null;
2727

2828
private static final Comparator<XYZPoint> X_COMPARATOR = new Comparator<XYZPoint>() {
29-
3029
/**
3130
* {@inheritDoc}
3231
*/
@@ -41,7 +40,6 @@ public int compare(XYZPoint o1, XYZPoint o2) {
4140
};
4241

4342
private static final Comparator<XYZPoint> Y_COMPARATOR = new Comparator<XYZPoint>() {
44-
4543
/**
4644
* {@inheritDoc}
4745
*/
@@ -56,7 +54,6 @@ public int compare(XYZPoint o1, XYZPoint o2) {
5654
};
5755

5856
private static final Comparator<XYZPoint> Z_COMPARATOR = new Comparator<XYZPoint>() {
59-
6057
/**
6158
* {@inheritDoc}
6259
*/
@@ -77,8 +74,7 @@ public int compare(XYZPoint o1, XYZPoint o2) {
7774
/**
7875
* Default constructor.
7976
*/
80-
public KdTree() {
81-
}
77+
public KdTree() { }
8278

8379
/**
8480
* Constructor for creating a more balanced tree. It uses the
@@ -91,6 +87,19 @@ public KdTree(List<XYZPoint> list) {
9187
root = createNode(list, k, 0);
9288
}
9389

90+
/**
91+
* Constructor for creating a more balanced tree. It uses the
92+
* "median of points" algorithm.
93+
*
94+
* @param list
95+
* of XYZPoints.
96+
* @param k
97+
* of the tree.
98+
*/
99+
public KdTree(List<XYZPoint> list, int k) {
100+
root = createNode(list, k, 0);
101+
}
102+
94103
/**
95104
* Create node from list of XYZPoints.
96105
*
@@ -115,40 +124,32 @@ else if (axis == Y_AXIS)
115124
Collections.sort(list, Z_COMPARATOR);
116125

117126
KdNode node = null;
127+
List<XYZPoint> less = new ArrayList<XYZPoint>(list.size());
128+
List<XYZPoint> more = new ArrayList<XYZPoint>(list.size());
118129
if (list.size() > 0) {
119130
int medianIndex = list.size() / 2;
120-
node = new KdNode(k, depth, list.get(medianIndex));
121-
List<XYZPoint> less = new ArrayList<XYZPoint>(list.size() - 1);
122-
List<XYZPoint> more = new ArrayList<XYZPoint>(list.size() - 1);
131+
node = new KdNode(list.get(medianIndex), k, depth);
123132
// Process list to see where each non-median point lies
124133
for (int i = 0; i < list.size(); i++) {
125134
if (i == medianIndex)
126135
continue;
127136
XYZPoint p = list.get(i);
137+
// Cannot assume points before the median are less since they could be equal
128138
if (KdNode.compareTo(depth, k, p, node.id) <= 0) {
129139
less.add(p);
130140
} else {
131141
more.add(p);
132142
}
133143
}
134-
if ((medianIndex - 1) >= 0) {
135-
// Cannot assume points before the median are less since they
136-
// could be equal
137-
// List<XYZPoint> less = list.subList(0, mediaIndex);
138-
if (less.size() > 0) {
139-
node.lesser = createNode(less, k, depth + 1);
140-
node.lesser.parent = node;
141-
}
144+
145+
if ((medianIndex - 1) >= 0 && less.size() > 0) {
146+
node.lesser = createNode(less, k, depth + 1);
147+
node.lesser.parent = node;
142148
}
143-
if ((medianIndex + 1) <= (list.size() - 1)) {
144-
// Cannot assume points after the median are less since they
145-
// could be equal
146-
// List<XYZPoint> more = list.subList(mediaIndex + 1,
147-
// list.size());
148-
if (more.size() > 0) {
149-
node.greater = createNode(more, k, depth + 1);
150-
node.greater.parent = node;
151-
}
149+
150+
if ((medianIndex + 1) <= (list.size() - 1) && more.size() > 0) {
151+
node.greater = createNode(more, k, depth + 1);
152+
node.greater.parent = node;
152153
}
153154
}
154155

@@ -176,7 +177,7 @@ public boolean add(T value) {
176177
if (KdNode.compareTo(node.depth, node.k, value, node.id) <= 0) {
177178
// Lesser
178179
if (node.lesser == null) {
179-
KdNode newNode = new KdNode(k, node.depth + 1, value);
180+
KdNode newNode = new KdNode(value, k, node.depth + 1);
180181
newNode.parent = node;
181182
node.lesser = newNode;
182183
break;
@@ -185,7 +186,7 @@ public boolean add(T value) {
185186
} else {
186187
// Greater
187188
if (node.greater == null) {
188-
KdNode newNode = new KdNode(k, node.depth + 1, value);
189+
KdNode newNode = new KdNode(value, k, node.depth + 1);
189190
newNode.parent = node;
190191
node.greater = newNode;
191192
break;
@@ -205,7 +206,7 @@ public boolean add(T value) {
205206
* @return True if tree contains value.
206207
*/
207208
public boolean contains(T value) {
208-
if (value == null)
209+
if (value == null || root == null)
209210
return false;
210211

211212
KdNode node = getNode(this, value);
@@ -253,7 +254,7 @@ private static final <T extends KdTree.XYZPoint> KdNode getNode(KdTree<T> tree,
253254
* @return True if value was removed from the tree.
254255
*/
255256
public boolean remove(T value) {
256-
if (value == null)
257+
if (value == null || root == null)
257258
return false;
258259

259260
KdNode node = getNode(this, value);
@@ -327,12 +328,12 @@ private static final List<XYZPoint> getTree(KdNode root) {
327328
* last nodes are equal distances.
328329
* @param value
329330
* to find neighbors of.
330-
* @return unmodifiable collection of T neighbors.
331+
* @return Collection of T neighbors.
331332
*/
332333
@SuppressWarnings("unchecked")
333334
public Collection<T> nearestNeighbourSearch(int K, T value) {
334-
if (value == null)
335-
return null;
335+
if (value == null || root == null)
336+
return Collections.EMPTY_LIST;
336337

337338
// Map used for results
338339
TreeSet<KdNode> results = new TreeSet<KdNode>(new EuclideanComparator(value));
@@ -370,11 +371,10 @@ public Collection<T> nearestNeighbourSearch(int K, T value) {
370371
Collection<T> collection = new ArrayList<T>(K);
371372
for (KdNode kdNode : results)
372373
collection.add((T) kdNode.id);
373-
return Collections.unmodifiableCollection(collection);
374+
return collection;
374375
}
375376

376-
private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K,
377-
TreeSet<KdNode> results, Set<KdNode> examined) {
377+
private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K, TreeSet<KdNode> results, Set<KdNode> examined) {
378378
examined.add(node);
379379

380380
// Search node
@@ -457,7 +457,7 @@ public String toString() {
457457

458458
protected static class EuclideanComparator implements Comparator<KdNode> {
459459

460-
private XYZPoint point = null;
460+
private final XYZPoint point;
461461

462462
public EuclideanComparator(XYZPoint point) {
463463
this.point = point;
@@ -480,19 +480,22 @@ else if (d2.compareTo(d1) < 0)
480480

481481
public static class KdNode implements Comparable<KdNode> {
482482

483-
private int k = 3;
484-
private int depth = 0;
485-
private XYZPoint id = null;
483+
private final XYZPoint id;
484+
private final int k;
485+
private final int depth;
486+
486487
private KdNode parent = null;
487488
private KdNode lesser = null;
488489
private KdNode greater = null;
489490

490491
public KdNode(XYZPoint id) {
491492
this.id = id;
493+
this.k = 3;
494+
this.depth = 0;
492495
}
493496

494-
public KdNode(int k, int depth, XYZPoint id) {
495-
this(id);
497+
public KdNode(XYZPoint id, int k, int depth) {
498+
this.id = id;
496499
this.k = k;
497500
this.depth = depth;
498501
}
@@ -553,9 +556,9 @@ public String toString() {
553556

554557
public static class XYZPoint implements Comparable<XYZPoint> {
555558

556-
double x = Double.NEGATIVE_INFINITY;
557-
double y = Double.NEGATIVE_INFINITY;
558-
double z = Double.NEGATIVE_INFINITY;
559+
private final double x;
560+
private final double y;
561+
private final double z;
559562

560563
public XYZPoint(double x, double y) {
561564
this.x = x;
@@ -569,6 +572,16 @@ public XYZPoint(double x, double y, double z) {
569572
this.z = z;
570573
}
571574

575+
public double getX() {
576+
return x;
577+
}
578+
public double getY() {
579+
return y;
580+
}
581+
public double getZ() {
582+
return z;
583+
}
584+
572585
/**
573586
* Computes the Euclidean distance from this point to the other.
574587
*

0 commit comments

Comments
 (0)