I'm not sure if this is useful (or even stupid), but I thought about it:
You use sorting to sort ALL elements in the grid and then select the first k elements. If you consider the sorting algorithm as a recursive merge-sorting, you have something like this:
- Half-split split
- Count on both halves
- Combine both sorted halves
Perhaps you could optimize such a function for your needs. The unifying part usually combines all the elements from both halves, but you are only interested in the first k , which is the result of the merger. Thus, you can merge only until you have the elements k and ignore the rest.
So, in the worst case, where k >= n ( n is the grid size), you will still only have the difficulty of sorting the merge. O(n log n) Honestly, I cannot determine the complexity of this solution with respect to k . (too tired for this at the moment)
Here is an example implementation of this solution (it is definitely not optimal and not generalized):
def minK(seq: IndexedSeq[coord], x: coord, k: Int) = { val dist = (c: coord) => c.dist(x) def sort(seq: IndexedSeq[coord]): IndexedSeq[coord] = seq.size match { case 0 | 1 => seq case size => { val (left, right) = seq.splitAt(size / 2) merge(sort(left), sort(right)) } } def merge(left: IndexedSeq[coord], right: IndexedSeq[coord]) = { val leftF = left.lift val rightF = right.lift val builder = IndexedSeq.newBuilder[coord] @tailrec def loop(leftIndex: Int = 0, rightIndex: Int = 0): Unit = { if (leftIndex + rightIndex < k) { (leftF(leftIndex), rightF(rightIndex)) match { case (Some(leftCoord), Some(rightCoord)) => { if (dist(leftCoord) < dist(rightCoord)) { builder += leftCoord loop(leftIndex + 1, rightIndex) } else { builder += rightCoord loop(leftIndex, rightIndex + 1) } } case (Some(leftCoord), None) => { builder += leftCoord loop(leftIndex + 1, rightIndex) } case (None, Some(rightCoord)) => { builder += rightCoord loop(leftIndex, rightIndex + 1) } case _ => } } } loop() builder.result } sort(seq) }
Kigyo
source share