详细说明我的评论(可能还有Anony-Mousse's answer):
在构建 KD-trees 中预排序的关键思想是在拆分期间保持顺序。开销看起来相当高,使用 re-sorting(和 k-select)的比较基准似乎是有序的。
一些证明原理的Java源代码:
package net.*.coder.greybeard.sandbox;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
/** finger exercise pre-sorting & split for KD-tree construction
* (re. https://stackoverflow.com/q/35225509/3789665) */
public class KDPreSort {
/** K-dimensional key, dimensions fixed
* by number of coordinates in construction */
static class KKey {
public static KKey[] NONE = {};
final Comparable[]coordinates;
public KKey(Comparable ...coordinates) {
this.coordinates = coordinates;
}
/** @return {@code Comparator<KKey>} for coordinate {@code n}*/
static Comparator<KKey> comparator(int n) { // could be cached
return new Comparator<KDPreSort.KKey>() { @Override
public int compare(KKey l, KKey r) {
return l.coordinates[n]
.compareTo(r.coordinates[n]);
}
};
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(
Arrays.deepToString(coordinates));
sb.setCharAt(0, '(');
sb.setCharAt(sb.length()-1, ')');
return sb.toString();
}
}
// static boolean trimLists = true; // introduced when ArrayList was used in interface
/** @return two arrays of {@code KKey}s: comparing smaller than
* or equal to {@code pivot} (according to {@code comp)},
* and greater than pivot -
* in the same order as in {@code keys}. */
static KKey[][] split(KKey[] keys, KKey pivot, Comparator<KKey> comp) {
int length = keys.length;
ArrayList<KKey>
se = new ArrayList<>(length),
g = new ArrayList<>(length);
for (KKey k: keys) {
// pick List to add to
List<KKey>d = comp.compare(k, pivot) <= 0 ? se : g;
d.add(k);
}
// if (trimLists) { se.trimToSize(); g.trimToSize(); }
return new KKey[][] { se.toArray(KKey.NONE), g.toArray(KKey.NONE) };
}
/** @return two arrays of <em>k</em> arrays of {@code KKey}s:
* comparing smaller than or equal to {@code pivot}
* (according to {@code comp)}, and greater than pivot,
* in the same order as in {@code keysByCoordinate}. */
static KKey[][][]
splits(KKey[][] keysByCoordinate, KKey pivot, Comparator<KKey> comp) {
final int length = keysByCoordinate.length;
KKey[][]
se = new KKey[length][],
g = new KKey[length][],
splits;
for (int i = 0 ; i < length ; i++) {
splits = split(keysByCoordinate[i], pivot, comp);
se[i] = splits[0];
g[i] = splits[1];
}
return new KKey[][][] { se, g };
}
// demo
public static void main(String[] args) {
// from https://stackoverflow.com/q/17021379/3789665
Integer [][]coPairs = {// {0, 7}, {1, 3}, {3, 0}, {3, 1}, {6, 2},
{12, 21}, {13, 27}, {19, 5}, {39, 5}, {49, 63}, {43, 45}, {41, 22}, {27, 7}, {20, 12}, {32, 11}, {24, 56},
};
KKey[] someKeys = new KKey[coPairs.length];
for (int i = 0; i < coPairs.length; i++) {
someKeys[i] = new KKey(coPairs[i]);
}
//presort
Arrays.sort(someKeys, KKey.comparator(0));
List<KKey> x = new ArrayList<>(Arrays.asList(someKeys));
System.out.println("by x: " + x);
KKey pivot = someKeys[someKeys.length/2];
Arrays.sort(someKeys, KKey.comparator(1));
System.out.println("by y: " + Arrays.deepToString(someKeys));
// split by x
KKey[][] allOrdered = new KKey[][] { x.toArray(KKey.NONE), someKeys },
xSplits[] = splits(allOrdered, pivot, KKey.comparator(0));
for (KKey[][] c: xSplits)
System.out.println("split by x of " + pivot + ": "
+ Arrays.deepToString(c));
// split "higher x" by y
pivot = xSplits[1][1][xSplits[1][1].length/2];
KKey[][] ySplits[] = splits(xSplits[1], pivot, KKey.comparator(1));
for (KKey[][] c: ySplits)
System.out.println("split by y of " + pivot + ": "
+ Arrays.deepToString(c));
}
}
(在没有投入太多精力的情况下,没有在 SE 上找到合适的答案/实现。输出的示例无法令人信服,对于较长的示例,我不得不重新格式化以相信它。
代码看起来很丑,很可能是因为它是:如果愿意重新阅读licence of code posted on SE,请访问Code Review。)
(考虑有投票、接受和奖励赏金,并重新访问 Anony-Mousse 的答案。)