获取java数组中n个最大值的索引

Posted

技术标签:

【中文标题】获取java数组中n个最大值的索引【英文标题】:Get indices of n maximums in java array 【发布时间】:2013-07-11 12:01:35 【问题描述】:

我有一个大小为 1000 的数组。如何找到五个最大元素的索引(索引)?

下面显示了一个带有设置代码的示例和我的尝试:

Random rand = new Random();
int[] myArray = new int[1000];
int[] maxIndices = new int[5];
int[] maxValues = new int[5];

for (int i = 0; i < myArray.length; i++) 
  myArray[i] = rand.nextInt();


for (int i = 0; i < 5; i++) 
  maxIndices[i] = i;
  maxValues[i] = myArray[i];


for (int i = 0; i < maxIndices.length; i++) 
  for (int j = 0; j < myArray.length; j++) 
    if (myArray[j] > maxValues[i]) 
      maxIndices[i] = j;
      maxValues[i] = myArray[j];
    
  


for (int i = 0; i < maxIndices.length; i++) 
  System.out.println("Index: " + maxIndices[i]);

我知道问题在于它不断地将最高最大值分配给所有最大元素。我不确定如何解决这个问题,因为我必须保留 myArray 的值和索引。

我不认为排序是一种选择,因为我需要保留索引。事实上,正是我特别需要的索引。

【问题讨论】:

当你在top 5中找到新元素时,看起来你需要重新考虑如何更新。 this discussion中有一些保留索引的方法 (要清楚,你的方法已经非常接近正确了;你只需要重新设计第三个循环。) 你可以使用数组列表吗?这可能会导致更简单的算法 首先获取 first 最大值的索引。 【参考方案1】:

排序是一种选择,但会消耗额外的内存。考虑以下算法。

1. Allocate additional array and copy into - O(n)
2. Sort additional array - O(n lg n)
3. Lop off the top k elements (in this case 5) - O(n), since k could be up to n
4. Iterate over the original array - O(n)
    4.a search the top k elements for to see if they contain the current element - O(lg n)

所以第 4 步是 (n * lg n),就像排序一样。整个算法为n lg n,代码非常简单。

这是一个快速而肮脏的例子。其中可能存在错误,显然 null 检查等会起作用。

导入 java.util.Arrays;

class ArrayTest 

    public static void main(String[] args) 
        int[] arr = 1, 3, 5, 7, 9, 2, 4, 6, 8, 10;
        int[] indexes = indexesOfTopElements(arr,3);
        for(int i = 0; i < indexes.length; i++) 
            int index = indexes[i];
            System.out.println(index + " " + arr[index]);
        
    

    static int[] indexesOfTopElements(int[] orig, int nummax) 
        int[] copy = Arrays.copyOf(orig,orig.length);
        Arrays.sort(copy);
        int[] honey = Arrays.copyOfRange(copy,copy.length - nummax, copy.length);
        int[] result = new int[nummax];
        int resultPos = 0;
        for(int i = 0; i < orig.length; i++) 
            int onTrial = orig[i];
            int index = Arrays.binarySearch(honey,onTrial);
            if(index < 0) continue;
            result[resultPos++] = i;
        
        return result;
    


您还可以采取其他措施来减少此操作的开销。例如,您可以选择使用仅跟踪最大 5 的队列而不是排序。作为ints,它们的值可能必须装箱才能添加到集合中(除非您自己滚动),这会增加开销显着。

【讨论】:

@TheNewIdiot - 正确的用法是 lg 而不是 log。并且 4.a 与 5 不同,因为它不是一个不同的步骤——它是在迭代过程中发生的。 @Hunter 那种。 lg 表示 log base 2。当使用大的O 表示法时,它们确实会相互压缩,因为它们的顺序相同,这是正确的。但是,如果该更改已经过审核,它将被拒绝为“太小”,而将 4.a 更改为 5 将被拒绝为“不正确”。 @corsiKa 正是我所需要的。谢谢。【参考方案2】:

很抱歉回答这个老问题,但我缺少一个具有以下所有属性的实现:

易于阅读 高性能 处理多个相同的值

因此我实现了它:

    private int[] getBestKIndices(float[] array, int num) 
        //create sort able array with index and value pair
        IndexValuePair[] pairs = new IndexValuePair[array.length];
        for (int i = 0; i < array.length; i++) 
            pairs[i] = new IndexValuePair(i, array[i]);
        

        //sort
        Arrays.sort(pairs, new Comparator<IndexValuePair>() 
            public int compare(IndexValuePair o1, IndexValuePair o2) 
                return Float.compare(o2.value, o1.value);
            
        );

        //extract the indices
        int[] result = new int[num];
        for (int i = 0; i < num; i++) 
            result[i] = pairs[i].index;
        
        return result;
    

    private class IndexValuePair 
        private int index;
        private float value;

        public IndexValuePair(int index, float value) 
            this.index = index;
            this.value = value;
        
    

【讨论】:

【参考方案3】:

回答有点晚了,你也可以用我写的这个函数:

/**
  * Return the indexes correspond to the top-k largest in an array.
  */
public static int[] maxKIndex(double[] array, int top_k) 
    double[] max = new double[top_k];
    int[] maxIndex = new int[top_k];
    Arrays.fill(max, Double.NEGATIVE_INFINITY);
    Arrays.fill(maxIndex, -1);

    top: for(int i = 0; i < array.length; i++) 
        for(int j = 0; j < top_k; j++) 
            if(array[i] > max[j]) 
                for(int x = top_k - 1; x > j; x--) 
                    maxIndex[x] = maxIndex[x-1]; max[x] = max[x-1];
                
                maxIndex[j] = i; max[j] = array[i];
                continue top;
            
        
    
    return maxIndex;

【讨论】:

尝试使用一些条件来避免继续:programmers.stackexchange.com/questions/58237/…【参考方案4】:

我的快速和有点“跳出框框思考”的想法是使用最多包含 5 个元素的EvictingQueue。您必须使用数组中的前五个元素预先填充它(按升序进行,因此您添加的第一个元素是五个元素中最低的)。

当当前值大于队列中的最小值时,您必须遍历数组并将新元素添加到队列中。要记住索引,请创建一个包装对象(值/索引对)。

遍历整个数组后,队列中有五个最大值/索引对(按降序排列)。

这是一个 O(n) 的解决方案。

【讨论】:

和我的答案很像xD【参考方案5】:

Arrays.sort(myArray),然后取最后 5 个元素。

如果您想保留原始订单,请排序副本。

如果您想要索引,则没有像 python 或其他一些语言那样快速而简单的解决方案。你排序和扫描,但这很难看。

或者你可以反对——毕竟这是java。 制作一个 ArrayMaxFilter 对象。它将有一个私有类 ArrayElement,它由一个索引和一个值组成,并且按值自然排序。它将有一个方法,该方法采用一对整数、索引和值,创建它们的 ArrayElement,并将它们放入长度为 5 的优先级队列中。(或者你想找到的任何数量)。从数组中提交每个索引/值对,然后报告出队列中剩余的值。 (是的,优先级队列传统上保持最低值,但你可以在你的实现中翻转它)

【讨论】:

OP 希望保留原始数组中的索引。【参考方案6】:

这是我的解决方案。创建一个将索引与值配对的类:

public class IndiceValuePair
    private int indice;
    private int value;

    public IndiceValuePair(int ind, int val)
        indice = ind;
        value = val;
    
    public int getIndice()
        return indice;
    
    public int getValue()
        return value;
    

然后在你的 main 方法中使用这个类:

public static void main(String[] args)
    Random rand = new Random();
    int[] myArray = new int[10];
    IndiceValuePair[] pairs = new IndiceValuePair[5];
    System.out.println("Here are the indices and their values:");
    for(int i = 0; i < myArray.length; i++) 
        myArray[i] = rand.nextInt(100);
        System.out.println(i+ ": " + myArray[i]);
        for(int j = 0; j < pairs.length; j++)
            //for the first five entries
            if(pairs[j] == null)
                pairs[j] = new IndiceValuePair(i, myArray[i]);
                break;
            
            else if(pairs[j].getValue() < myArray[i])
                //inserts the new pair into its correct spot
                for(int k = 4; k > j; k--)
                    pairs[k] = pairs [k-1];
                
                pairs[j] = new IndiceValuePair(i, myArray[i]);
                break;
            
        
    
    System.out.println("\n5 Max indices and their values");
    for(int i = 0; i < pairs.length; i++)
        System.out.println(pairs[i].getIndice() + ": " + pairs[i].getValue());
    

以及运行的示例输出:

Here are the indices and their values:
0: 13
1: 71
2: 45
3: 38
4: 43
5: 9
6: 4
7: 5
8: 59
9: 60

5 Max indices and their values
1: 71
9: 60
8: 59
2: 45
4: 43

我提供的示例只生成了 10 个值在 0 到 99 之间的整数,以便我可以看到它有效。您可以轻松更改它以适应 1000 个任意大小的值。此外,我没有运行 3 个单独的 for 循环,而是检查了我添加的最新值是否是在我添加到 myArray 之后的最大值。试一试,看看它是否适合你

【讨论】:

以上是关于获取java数组中n个最大值的索引的主要内容,如果未能解决你的问题,请参考以下文章

Python | 快速获取某一列数组中前 N 个最大值/最小值的索引 | 三种方法总结

Java中数组获取最大值

查找数组中最大数字的索引

列举几个关于Java Collections的常见问题并给出答案

如何从java中的数组中打印多个最大值[重复]

java如何在数组中取最大值和最小值