[英]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.