繁体   English   中英

排序后获取未排序的双精度数组的索引

[英]Getting the indices of an unsorted double array after sorting

这个问题来,因为这的同伴一个是认为最快的双数组排序。

现在,我想获取对应于未排序数组的前k索引。

我已经实现了这个版本,它(不幸)使用自动装箱和HashMap ,如一些答案,包括这个建议一个

HashMap<Double, Integer> map = new HashMap<Double, Integer>();
for(int i = 0; i < numClusters; i++) {
    map.put(scores[i], i);
}
Arrays.sort(scores);
HashSet<Integer> topPossibleClusters = new HashSet<Integer>();
for(int i = 0; i < numClusters; i++) {
    topPossibleClusters.add(map.get(scores[numClusters - (i+1)]));
}

如您所见,这使用HashMap ,其键具有原始数组的Double值和原始数组的索引作为键的值。 因此,在对原始数组进行排序之后,我只是从map检索它。

我也使用HashSet因为我有兴趣使用.contains()方法确定此集合中是否包含int (我不知道这是否有区别,因为正如我在另一个问题中提到的那样,我的数组很小-50个元素-)。 如果没有什么区别,请指出。

我对价值本身不感兴趣,仅对指数感兴趣。

我的问题是,是否有更快的方法?

这种相互链接/互锁的集合使自己容易碎,容易损坏,难以调试,无法维护的代码。

而是创建一个对象:

class Data {
    double value;
    int originalIndex;
}

创建一个存储原始值和索引的Data对象数组。

使用自定义比较器对它们进行排序,该比较器查看data.value并对降序进行排序。

现在,数组中最重要的X项就是所需的项,您可以根据需要查看valueoriginalIndex

正如Tim指出的那样,链接多个集合很容易出错。 我建议使用TreeMap因为这将允许一个独立的解决方案。

假设您有double[] data ,首先将其复制到TreeMap

final TreeMap<Double, Integer> dataWithIndex = new TreeMap<>();
for(int i = 0; i < data.length; ++i) {
    dataWithIndex.put(data[i], i);
}

注意:您可以将dataWithIndex声明为NavigableMap不太具体,但是它要长得多,并且实际上并没有增加太多,因为JDK中只有一个实现。

这将在O(n lg n)时间内填充Map ,因为每个put均为O(lg n) -这与排序的复杂度相同。 实际上,它可能会稍微慢一些,但它会以相同的方式扩展

现在,假设您需要第k元素,首先需要找到第k个元素-这是O(k)

final Iterator<Double> keyIter = dataWithIndex.keySet().iterator();
double kthKey;
for (int i = 0; i < k; ++i) {
    kthKey = keyIter.next();
}

现在,您只需要获取具有所有条目直到第k个条目的子映射:

final Map<Double, Integer> topK = dataWithIndex.headMap(kthKey, true);

如果只需要执行一次,那么使用Java 8可以执行以下操作:

List<Entry<Double, Integer>> topK = IntStream.range(0, data.length).
        mapToObj(i -> new SimpleEntry<>(data[i], i)).
        sorted(comparing(Entry::getKey)).
        limit(k).
        collect(toList());

IntStream ,使用IntStream获取data索引,并将mapToObjdata[i] => iEntry (使用AbsractMap.SimpleEntry实现)。 现在使用Entry::getKey排序,并将Stream的大小限制为k个条目。 现在,只需将结果收集到List 这具有不破坏data阵列中重复项的优点。

这几乎完全是Tim在他的答案中所建议的,但是使用了现有的JDK类。

该方法也是O(n lg n) 要注意的是,如果重用TreeMap方法,则O(n lg n)来构建Map而只有O(k)可以重用它。 如果要重复使用Java 8解决方案,则可以执行以下操作:

List<Entry<Double, Integer>> sorted = IntStream.range(0, data.length).
        mapToObj(i -> new SimpleEntry<>(data[i], i)).
        sorted(comparing(Entry::getKey)).
        collect(toList());

即不要将大小限制为k元素。 现在,要获取前k元素,您只需要做:

List<Entry<Double, Integer>> subList = sorted.subList(0, k);

这样做的魔力在于它是O(1)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM