这个问题是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元素 - )。如果这没有区别,可以指出它。
我对价值本身不感兴趣,只对指数感兴趣。
我的问题是,是否有更快的方法可以使用它?
答案 0 :(得分:3)
这种互连/互锁集合使其易于破解,易于破解,难以调试,无法维护的代码。
而是创建一个对象:
class Data {
double value;
int originalIndex;
}
创建一个存储原始值和索引的Data对象数组。
使用自定义比较器对它们进行排序,该比较器查看data.value并按降序排序。
现在,数组中的前X项是您想要的,您可以根据需要查看value
和originalIndex
。
答案 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());
即。将IntStream
和data
的索引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);
。