Source: AMAZON INTERVIEW QUESTION
Given a point P and other N points in two dimensional space, find K points
What is wrong with below approach ?
1) Calculate distance from given point to other points.
2) Store the distance and index of that point to TreeMap<Double,Integer> map
3) Select top K elements from map. It's values will give index of Point element from points array.
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time,
Solution 1 make heap of size K and collect points by minimal distance O(NLogK) complexity.
Solution 2: Take and array of size N and Sort by distance. Should be used QuickSort (Hoare modification). As answer take first K points. This is too NlogN complexity but it is possible optimize to approximate O(N). If skip sorting of unnecessary sub arrays. When you split array by 2 sub arrays you should take only array where Kth index located. complexity will be : N +N/2 +N/4 + ... = O(N).
Solution 3: search Kth element in result array and takes all point lesser then founded. Exists O(N) alghoritm, similar to search of median.
Notes: better use sqr of distance to avoid of sqrt operations, it will be greater faster if point has integer coordinates.
As interview answer better use Solution 2 or 3.
Solution 1
private List<Point> nearestKPoint_1(List<Point> list, final Point center, int k) {
List<Point> ans = new ArrayList<>();
PriorityQueue<Point> maxHeap = new PriorityQueue<>(k + 1, new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
return distance(center, o2) - distance(center, o1);
}
});
for (Point p : list) {
maxHeap.offer(p);
if (maxHeap.size() > k) {
maxHeap.poll();
}
}
Iterator<Point> i = maxHeap.iterator();
while (i.hasNext()) {
ans.add(i.next());
}
return ans;
}
public int distance(Point p1, Point p2) {
return (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
}
static class Point {
int x;
int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
if (x != point.x) return false;
return y == point.y;
}
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
return result;
}
}
Solution 2
private List<Point> nearestKPoint_2(List<Point> list, final Point center, int k) {
List<Point> ans = new ArrayList<>();
Distance[] nums = new Distance[list.size()];
for (int i = 0; i < nums.length; i++) {
nums[i] = new Distance(distance(center, list.get(i)), i);
}
quickSelect(nums, k);
for (int i = 0; i < k; i++) {
ans.add(list.get(nums[i].i));
}
return ans;
}
private void quickSelect(Distance[] nums, int k) {
int start = 0, end = nums.length - 1;
while (start < end) {
int p = partition(nums, start, end);
if (p == k) {
return;
} else if (p < k) {
start = p + 1;
} else {
end = p - 1;
}
}
}
private int partition(Distance[] nums, int start, int end) {
Distance pivot = nums[start];
int i = start, j = end + 1;
while (true) {
while (i < end && nums[++i].compareTo(pivot) < 0);
while (j > start && nums[--j].compareTo(pivot) > 0);
if (i >= j) {
break;
}
swap(nums, i, j);
}
swap(nums, start, j);
return j;
}
private void swap(Distance[] nums, int i, int j) {
Distance tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
class Distance implements Comparable<Distance> {
int d;
int i;
public Distance(int d, int i) {
this.d = d;
this.i = i;
}
@Override
public int compareTo(Distance o) {
return this.d - o.d;
}
}