算法平衡K-D树与O(kn log n)

时间:2016-02-05 13:32:30

标签: arrays algorithm tree kdtree

我尝试用O(kn log n)实现平衡的KD树,我使用预先排序的K数组(每个索引的排序数组)得到O(kn log n),并使用中位数得到平衡树。

我面临的问题是,大多数某个级别的中值,例如x轴的中位数,可能会在另一个后续级别再次选择,例如y轴。

我尝试通过使用选择的x值作为枢轴将y排序数组划分为两个数组来解决此问题,但这种方式不会产生平衡树。

知道如何用O(kn log n)得到K-D平衡树吗?

修改

引自wiki https://en.wikipedia.org/wiki/K-d_tree

  

用于构建平衡k-d树的替代算法预先分配   构建树之前的数据。然后他们保持了秩序   在树木施工期间预先分配,因此消除了昂贵的步骤   在每个细分水平找到中位数。两个这样的   算法构建一个平衡的k-d树来对三角形进行排序   改善三维光线追踪的执行时间   电脑图像。这些算法在之前预先排列n个三角形   构建k-d树,然后在O(n log n)时间内构建树   最好的情况。[5] [6]构建平衡k-d树以进行排序的算法   点具有O(kn log n)的最坏情况复杂度。[7]这个算法   使用O(n log n)排序对k个维度中的每个维度中的n个点进行排序   例如在构建树之前的Heapsort或Mergesort。然后呢   在树木建造过程中保持这些k预定的顺序   从而避免在每个细分级别找到中位数。

任何人都可以提供上面提供的算法吗?

修改

提出了一种方法,但如果中位数的特定轴有任何重复值,则无效。

例如

x1 = [(0,7),(1,3),(3,0),(3,1),(6,2)] y1 = [(3,0),(3,1) ,(6,2),(1,3),(0,7)]

x轴的中位数为3。 因此,当我们想要分割数组y11和y12时,我们必须使用>和<将pivot作为分隔符左右分布y数组。

如果特定轴上的中位数a重复,则无法保证其中一个是正确的

考虑x轴上的分区,完成上述第一步分区示例后x1阵列没有问题:

median=(3,0)
The pivot = 3 // is it's the median of x axis
y11[],y12[] 
for(i = 0 ; i < x1.size;i++)
  if(y1[i].getX()<pivot)
    y11.add(y1[i])
  else 
    if(y1[i].getX()>pivot)
     y12.add(y1[i])

这将导致y11 = [(2,1),(1,3),(0,7)] y12 = [(6,2)]

知道如何处理这种情况吗? 或者是否还有其他预分类kd树预分类算法O(kn log n)?

2 个答案:

答案 0 :(得分:1)

拆分数据时,您需要保留排序顺序。

E.g。使用我们构建的数据(x,y)

x1 = [ (0, 7), (1, 3), (3, 0), (4, 2), (6, 1) ]
y1 = [ (3, 0), (6, 1), (3, 2), (1, 3), (0, 7) ]

如果我们现在在x处拆分,我们需要通过x=3,y=0处的记录过滤这两个集合。

即。拆分两个列表,删除(3,0),所有带x<3的项目分别转到第一个列表,所有x>3转到第二个(订单不变):

x1 -> filter to  x11 = [ (0, 7), (1, 3) ]  x12 = [ (4, 2), (6, 1) ]
y1 -> filter to  y11 = [ (1, 3), (0, 7) ]  y12 = [ (6, 1), (4, 2) ]

重点是按x值过滤每个排序列表,而保持排序顺序(因此在每个O(log n)级别中都是O(n * k))。如果仅使用x1,并从x1重建y11和y12,则需要再次排序。必要时,它就像你用x排序一次,y一次排序。除非我们再次排序,否则只选择。

我不认为这在实践中好得多。排序比额外的内存便宜。

答案 1 :(得分:1)

详细说明我的评论(可能是Anony-Mousse's answer):

在构建KD树时预先排序的关键思想是在拆分期间保持顺序。开销看起来很高,与 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的答案。)