[英]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项就是所需的项,您可以根据需要查看value
和originalIndex
。
正如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
索引,并将mapToObj
到data[i] => i
的Entry
(使用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.