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

时间:2014-09-12 09:01:50

标签: java arrays sorting indices

这个问题是one的伴侣,它认为双数组的排序速度最快。

现在我想获得与未排序数组相对应的top - k索引。

我已经实现了这个版本(不幸的是)使用了自动装箱和HashMap在一些答案中提出的建议,包括one

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因为我有兴趣使用int方法决定此集合中是否包含.contains()。 (我不知道这是否有所不同,因为我在另一个问题中提到我的数组是小-50元素 - )。如果这没有区别,可以指出它。

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

我的问题是,是否有更快的方法可以使用它?

2 个答案:

答案 0 :(得分:3)

这种互连/互锁集合使其易于破解,易于破解,难以调试,无法维护的代码。

而是创建一个对象:

class Data {
    double value;
    int originalIndex;
}

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

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

现在,数组中的前X项是您想要的,您可以根据需要查看valueoriginalIndex

答案 1 :(得分:1)

由于链接多个集合的Tim points out相当错误。我建议使用TreeMap,因为这将允许一个独立的解决方案。

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

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

N.B。您可以将dataWithIndex声明为NavigableMap,使其不那么具体,但由于JDK中只有一个实现,所以它会更长,而且实际上并没有太多增加。

这会在Map时填充O(n lg n),因为每个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());

即。将IntStreamdata的索引mapToObj转换为Entry的{​​{1}}(使用data[i] => i实施)。现在使用AbsractMap.SimpleEntry对其进行排序,并将Entry::getKey的大小限制为Stream条目。现在只需将结果收集到k。这样做的好处是不会破坏List数组中的重复条目。

这几乎正是Tim在他的回答中所建议的,但是使用现有的JDK类。

此方法也是data。问题在于,如果O(n lg n)方法被重用,则TreeMap构建O(n lg n)但仅Map来重用它。如果您想重用Java 8解决方案,那么您可以这样做:

O(k)

即。不要将大小限制为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);