I recently coded KDTree to find the closest neighbor in three-dimensional space and ran into the same problems that NNS understands, especially 3.2 from the wiki. I ended up using this algorithm, which seems to work in all my tests:
Here is the initial sheet search:
public Collection<T> nearestNeighbourSearch(int K, T value) { if (value==null) return null; //Map used for results TreeSet<KdNode> results = new TreeSet<KdNode>(new EuclideanComparator(value)); //Find the closest leaf node KdNode prev = null; KdNode node = root; while (node!=null) { if (KdNode.compareTo(node.depth, node.k, node.id, value)<0) { //Greater prev = node; node = node.greater; } else { //Lesser prev = node; node = node.lesser; } } KdNode leaf = prev; if (leaf!=null) { //Used to not re-examine nodes Set<KdNode> examined = new HashSet<KdNode>(); //Go up the tree, looking for better solutions node = leaf; while (node!=null) { //Search node searchNode(value,node,K,results,examined); node = node.parent; } } //Load up the collection of the results Collection<T> collection = new ArrayList<T>(K); for (KdNode kdNode : results) { collection.add((T)kdNode.id); } return collection; }
Here is a recursive search starting at the nearest node sheet:
private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K, TreeSet<KdNode> results, Set<KdNode> examined) { examined.add(node); //Search node KdNode lastNode = null; Double lastDistance = Double.MAX_VALUE; if (results.size()>0) { lastNode = results.last(); lastDistance = lastNode.id.euclideanDistance(value); } Double nodeDistance = node.id.euclideanDistance(value); if (nodeDistance.compareTo(lastDistance)<0) { if (results.size()==K && lastNode!=null) results.remove(lastNode); results.add(node); } else if (nodeDistance.equals(lastDistance)) { results.add(node); } else if (results.size()<K) { results.add(node); } lastNode = results.last(); lastDistance = lastNode.id.euclideanDistance(value); int axis = node.depth % node.k; KdNode lesser = node.lesser; KdNode greater = node.greater; //Search children branches, if axis aligned distance is less than current distance if (lesser!=null && !examined.contains(lesser)) { examined.add(lesser); double nodePoint = Double.MIN_VALUE; double valuePlusDistance = Double.MIN_VALUE; if (axis==X_AXIS) { nodePoint = node.id.x; valuePlusDistance = value.x-lastDistance; } else if (axis==Y_AXIS) { nodePoint = node.id.y; valuePlusDistance = value.y-lastDistance; } else { nodePoint = node.id.z; valuePlusDistance = value.z-lastDistance; } boolean lineIntersectsCube = ((valuePlusDistance<=nodePoint)?true:false); //Continue down lesser branch if (lineIntersectsCube) searchNode(value,lesser,K,results,examined); } if (greater!=null && !examined.contains(greater)) { examined.add(greater); double nodePoint = Double.MIN_VALUE; double valuePlusDistance = Double.MIN_VALUE; if (axis==X_AXIS) { nodePoint = node.id.x; valuePlusDistance = value.x+lastDistance; } else if (axis==Y_AXIS) { nodePoint = node.id.y; valuePlusDistance = value.y+lastDistance; } else { nodePoint = node.id.z; valuePlusDistance = value.z+lastDistance; } boolean lineIntersectsCube = ((valuePlusDistance>=nodePoint)?true:false); //Continue down greater branch if (lineIntersectsCube) searchNode(value,greater,K,results,examined); } }
The full source java can be found here .
source share