/// <summary> /// Uses the Round-Robin strategy to create a KdTree of the given dimension for the set of points. /// </summary> public KdTree(params Point[] points) { if (points == null) { throw new ArgumentNullException(); } if (!points.Any()) { throw new ArgumentOutOfRangeException(); } dimension = points.First().Dimension(); if (points.Any(p => p.Dimension() != dimension)) { throw new ArgumentException(); } Shuffle(points); // Partitions the points in the range [low, high] into a subtree defined by the cutting dimension l Node KdTree(int low, int high, int l) { if (high < low) { return(null); } var byLevel = new Point.ByDimension(l); QuickSort(points, low, high, byLevel); var mid = low + (high - low) / 2; var newL = (l + 1) % dimension; return(new Node { point = points[mid], left = KdTree(low, mid - 1, newL), right = KdTree(mid + 1, high, newL) }); } root = KdTree(0, points.Length - 1, 0); }
/// <summary> /// Remove a point <code>p</code> from the tree. /// raises <exception cref="System.InvalidOperationException"> if the point does not exist. /// </summary> public void Delete(Point p) { // Implementation ported from CMU Bioinformatics lecture slides (offered by Carl Kingsford) if (p == null) { throw new ArgumentNullException(); } if (p.Dimension() != dimension) { throw new ArgumentException(); } // Returns the smallest point wrt to the given dimension d // in the tree rooted at n (here l is the cutting dimension of n) Point Min(Node n, int d, int l) { if (n == null) { return(null); } var newL = (l + 1) % dimension; if (l == d) { // If we are on the correct plane then we only need to inspect the left branch if (n.left == null) { return(n.point); } else { return(Min(n.left, d, newL)); } } else { // Otherwise we need to take a look at both branches var leftPt = Min(n.left, d, newL); var rightPt = Min(n.right, d, newL); var byDim = new Point.ByDimension(d); if (leftPt == null && rightPt == null) { return(n.point); } else if (leftPt == null) { return(byDim.Min(n.point, rightPt)); } else if (rightPt == null) { return(byDim.Min(n.point, leftPt)); } else { return(byDim.Min(n.point, leftPt, rightPt)); } } } // Delete q from the tree rooted at n (here l is the cutting dimension of n), and return the new subtree Node Delete(Point q, Node n, int l) { if (n == null) { throw new InvalidOperationException(); // Not found (it is OK to throw here) } var newL = (l + 1) % dimension; if (n.point == q) { // Found the point if (n.right != null) { // Replace the point with the smallest one in the right branch of n n.point = Min(n.right, l, newL); // n.b. Cannot be null, by design n.right = Delete(n.point, n.right, newL); } else if (n.left != null) { // Only have a left branch, so move its contents to the right n.point = Min(n.left, l, newL); n.right = Delete(n.point, n.left, newL); n.left = null; } else { n = null; } } else if (q[l] < n.point[l]) { n.left = Delete(q, n.left, newL); } else { n.right = Delete(q, n.right, newL); } return(n); } root = Delete(p, root, 0); }